home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume25 / trn / part07 < prev    next >
Encoding:
Internet Message Format  |  1991-12-02  |  87.1 KB

  1. Subject:  v25i010:  trn 2.0 - threaded newsreader based on rn 4.4, Part07/13
  2. Newsgroups: comp.sources.unix
  3. Approved: vixie@pa.dec.com
  4.  
  5. Submitted-by: davison@borland.com (Wayne Davison)
  6. Posting-number: Volume 25, Issue 10
  7. Archive-name: trn/part07
  8.  
  9. #! /bin/sh
  10. # This is a shell archive.  Remove anything before this line, then unpack
  11. # it by saving it into a file and typing "sh file".  To overwrite existing
  12. # files, type "sh file -c".  You can also feed this as standard input via
  13. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  14. # will see the following message at the end:
  15. #        "End of archive 7 (of 13)."
  16. # Contents:  bits.c respond.c rt-rn.c rt-select.c
  17. # Wrapped by vixie@cognition.pa.dec.com on Tue Dec  3 16:34:54 1991
  18. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  19. if test -f 'bits.c' -a "${1}" != "-c" ; then 
  20.   echo shar: Will not clobber existing file \"'bits.c'\"
  21. else
  22. echo shar: Extracting \"'bits.c'\" \(18289 characters\)
  23. sed "s/^X//" >'bits.c' <<'END_OF_FILE'
  24. X/* $Id: bits.c,v 4.4 1991/09/09 20:18:23 sob Exp sob $
  25. X *
  26. X * $Log: bits.c,v $
  27. X * Revision 4.4  1991/09/09  20:18:23  sob
  28. X * release 4.4
  29. X *
  30. X *
  31. X * 
  32. X */
  33. X/* This software is Copyright 1991 by Stan Barber. 
  34. X *
  35. X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
  36. X * use this software as long as: there is no monetary profit gained
  37. X * specifically from the use or reproduction of this software, it is not
  38. X * sold, rented, traded or otherwise marketed, and this copyright notice is
  39. X * included prominently in any copy made. 
  40. X *
  41. X * The author make no claims as to the fitness or correctness of this software
  42. X * for any use whatsoever, and it is provided as is. Any use of this software
  43. X * is at the user's own risk. 
  44. X */
  45. X
  46. X#include "EXTERN.h"
  47. X#include "common.h"
  48. X#include "rcstuff.h"
  49. X#include "head.h"
  50. X#include "util.h"
  51. X#include "final.h"
  52. X#include "rn.h"
  53. X#include "cheat.h"
  54. X#include "ng.h"
  55. X#include "artio.h"
  56. X#include "intrp.h"
  57. X#include "ngdata.h"
  58. X#include "rcln.h"
  59. X#include "kfile.h"
  60. X#ifdef USETHREADS
  61. X#include "threads.h"
  62. X#include "rthreads.h"
  63. X#endif
  64. X#include "INTERN.h"
  65. X#include "bits.h"
  66. X
  67. X#ifdef DBM
  68. X#    ifdef NULL
  69. X#    undef NULL
  70. X#    endif
  71. X#    include <dbm.h>
  72. X#endif
  73. MEM_SIZE ctlsize;            /* size of bitmap in bytes */
  74. X
  75. void
  76. bits_init()
  77. X{
  78. X#ifdef DELAYMARK
  79. X    dmname = savestr(filexp(RNDELNAME));
  80. X#else
  81. X    ;
  82. X#endif
  83. X}
  84. X
  85. X/* checkpoint the .newsrc */
  86. X
  87. void
  88. checkpoint_rc()
  89. X{
  90. X#ifdef DEBUGGING
  91. X    if (debug & DEB_CHECKPOINTING) {
  92. X    fputs("(ckpt)",stdout);
  93. X    fflush(stdout);
  94. X    }
  95. X#endif
  96. X    if (doing_ng)
  97. X    restore_ng();            /* do not restore M articles */
  98. X    if (rc_changed)
  99. X    write_rc();
  100. X#ifdef DEBUGGING
  101. X    if (debug & DEB_CHECKPOINTING) {
  102. X    fputs("(done)",stdout);
  103. X    fflush(stdout);
  104. X    }
  105. X#endif
  106. X}
  107. X
  108. X/* reconstruct the .newsrc line in a human readable form */
  109. X
  110. void
  111. restore_ng()
  112. X{
  113. X    register char *s, *mybuf = buf;
  114. X    register ART_NUM i;
  115. X    ART_NUM count=0;
  116. X    int safelen = LBUFLEN - 16;
  117. X
  118. X    strcpy(buf,rcline[ng]);        /* start with the newsgroup name */
  119. X    s = buf + rcnums[ng] - 1;        /* use s for buffer pointer */
  120. X#ifdef USETHREADS
  121. X    *s++ = RCCHAR(rcchar[ng]);        /* put the requisite : or !*/
  122. X#else
  123. X    *s++ = rcchar[ng];            /* put the requisite : or !*/
  124. X#endif
  125. X    *s++ = ' ';                /* put the not-so-requisite space */
  126. X    for (i=1; i<=lastart; i++) {    /* for each article in newsgroup */
  127. X    if (s-mybuf > safelen) {    /* running out of room? */
  128. X        safelen *= 2;
  129. X        if (mybuf == buf) {        /* currently static? */
  130. X        *s = '\0';
  131. X        mybuf = safemalloc((MEM_SIZE)safelen + 16);
  132. X        strcpy(mybuf,buf);    /* so we must copy it */
  133. X        s = mybuf + (s-buf);
  134. X                    /* fix the pointer, too */
  135. X        }
  136. X        else {            /* just grow in place, if possible */
  137. X        char *newbuf;
  138. X
  139. X        newbuf = saferealloc(mybuf,(MEM_SIZE)safelen + 16);
  140. X        s = newbuf + (s-mybuf);
  141. X        mybuf = newbuf;
  142. X        }
  143. X    }
  144. X    if (!was_read(i))        /* still unread? */
  145. X        count++;            /* then count it */
  146. X    else {                /* article was read */
  147. X        ART_NUM oldi;
  148. X
  149. X        sprintf(s,"%ld",(long)i);    /* put out the min of the range */
  150. X        s += strlen(s);        /* keeping house */
  151. X        oldi = i;            /* remember this spot */
  152. X        do i++; while (i <= lastart && was_read(i));
  153. X                    /* find 1st unread article or end */
  154. X        i--;            /* backup to last read article */
  155. X        if (i > oldi) {        /* range of more than 1? */
  156. X        sprintf(s,"-%ld,",(long)i);
  157. X                    /* then it out as a range */
  158. X        s += strlen(s);        /* and housekeep */
  159. X        }
  160. X        else
  161. X        *s++ = ',';        /* otherwise, just a comma will do */
  162. X    }
  163. X    }
  164. X    if (*(s-1) == ',')            /* is there a final ','? */
  165. X    s--;                /* take it back */
  166. X    *s++ = '\0';            /* and terminate string */
  167. X#ifdef DEBUGGING
  168. X    if (debug & DEB_NEWSRC_LINE && !panic) {
  169. X    printf("%s: %s\n",rcline[ng],rcline[ng]+rcnums[ng]) FLUSH;
  170. X    printf("%s\n",mybuf) FLUSH;
  171. X    }
  172. X#endif
  173. X    free(rcline[ng]);            /* return old rc line */
  174. X    if (mybuf == buf) {
  175. X    rcline[ng] = safemalloc((MEM_SIZE)(s-buf)+1);
  176. X                    /* grab a new rc line */
  177. X    strcpy(rcline[ng], buf);    /* and load it */
  178. X    }
  179. X    else {
  180. X    mybuf = saferealloc(mybuf,(MEM_SIZE)(s-mybuf)+1);
  181. X                    /* be nice to the heap */
  182. X    rcline[ng] = mybuf;
  183. X    }
  184. X    *(rcline[ng] + rcnums[ng] - 1) = '\0';
  185. X    if (rcchar[ng] == NEGCHAR) {    /* did they unsubscribe? */
  186. X    printf(unsubto,ngname) FLUSH;
  187. X    toread[ng] = TR_UNSUB;        /* make line invisible */
  188. X    }
  189. X    else
  190. X    /*NOSTRICT*/
  191. X    toread[ng] = (ART_UNREAD)count;        /* remember how many unread there are */
  192. X}
  193. X
  194. X/* mark an article unread, keeping track of toread[] */
  195. X
  196. void
  197. onemore(artnum)
  198. ART_NUM artnum;
  199. X{
  200. X#ifdef DEBUGGING
  201. X    if (debug && artnum < firstbit) {
  202. X    printf("onemore: %d < %d\n",artnum,firstbit) FLUSH;
  203. X    return;
  204. X    }
  205. X#endif
  206. X    if (ctl_read(artnum)) {
  207. X    ctl_clear(artnum);
  208. X    ++toread[ng];
  209. X    }
  210. X}
  211. X
  212. X/* mark an article read, keeping track of toread[] */
  213. X
  214. void
  215. oneless(artnum)
  216. ART_NUM artnum;
  217. X{
  218. X#ifdef DEBUGGING
  219. X    if (debug && artnum < firstbit) {
  220. X    printf("oneless: %d < %d\n",artnum,firstbit) FLUSH;
  221. X    return;
  222. X    }
  223. X#endif
  224. X    if (!ctl_read(artnum)) {
  225. X    ctl_set(artnum);
  226. X    if (toread[ng] > TR_NONE)
  227. X        --toread[ng];
  228. X    }
  229. X}
  230. X
  231. X/* mark an article as unread, making sure that firstbit is properly handled */
  232. X/* cross-references are left as read in the other newsgroups */
  233. X
  234. void
  235. unmark_as_read()
  236. X{
  237. X    check_first(art);
  238. X#ifdef USETHREADS
  239. X    find_article(art);
  240. X    /* Keep selected_count accurate */
  241. X    if (ctl_read(art)) {
  242. X    if (p_art) {
  243. X        if (selected_roots[p_art->root]) {
  244. X        selected_count++;
  245. X        }
  246. X        root_article_cnts[p_art->root] = 1;
  247. X    } else
  248. X        unthreaded++;
  249. X    }
  250. X    scan_all_roots = FALSE;
  251. X#endif
  252. X    onemore(art);
  253. X#ifdef MCHASE
  254. X# ifdef USETHREADS
  255. X    if ((olden_days > 1 || !p_art || (p_art->flags & HAS_XREFS))
  256. X     && !parse_maybe(art))
  257. X# else
  258. X    if (!parse_maybe(art))
  259. X# endif
  260. X    chase_xrefs(art,FALSE);
  261. X#endif
  262. X}
  263. X
  264. X#ifdef USETHREADS
  265. X/* mark an article as read in this newsgroup and possibly chase xrefs.
  266. X** p_art must be set to the current article's data.
  267. X*/
  268. X
  269. void
  270. set_read(artnum,selected,chase_xrefs_flag)
  271. ART_NUM artnum;
  272. int selected;
  273. bool_int chase_xrefs_flag;
  274. X{
  275. X    if (!ctl_read(artnum)) {
  276. X    oneless(artnum);
  277. X    if (p_art->subject != -1)
  278. X        selected_count -= selected;
  279. X    }
  280. X    if (chase_xrefs_flag && (p_art->flags & HAS_XREFS)) {
  281. X    if (output_chase_phrase) {
  282. X#ifdef VERBOSE
  283. X        IF(verbose)
  284. X        fputs("\nChasing xrefs", stdout);
  285. X        ELSE
  286. X#endif
  287. X#ifdef TERSE
  288. X        fputs("\nXrefs", stdout);
  289. X#endif
  290. X        output_chase_phrase = 0;
  291. X    }
  292. X    putchar('.'), fflush(stdout);
  293. X    chase_xrefs(artnum, TRUE);
  294. X    }
  295. X}
  296. X
  297. X/* mark an article as unread in this newsgroup only.
  298. X** p_art must be set to the current article's data.
  299. X*/
  300. X
  301. void
  302. set_unread(artnum,selected)
  303. ART_NUM artnum;
  304. int selected;
  305. X{
  306. X    if (artnum >= absfirst) {
  307. X    check_first(artnum);
  308. X    if (ctl_read(artnum)) {
  309. X        onemore(artnum);
  310. X        selected_count += selected;
  311. X        root_article_cnts[p_art->root] = 1;
  312. X        scan_all_roots = FALSE;
  313. X    }
  314. X    }
  315. X}
  316. X#endif
  317. X
  318. X#ifdef DELAYMARK
  319. X/* temporarily mark article as read.  When newsgroup is exited, articles */
  320. X/* will be marked as unread.  Called via M command */
  321. X
  322. void
  323. delay_unmark(artnum)
  324. ART_NUM artnum;
  325. X{
  326. X    if (dmfp == Nullfp) {
  327. X    dmfp = fopen(dmname,"w+");
  328. X    if (dmfp == Nullfp) {
  329. X        printf(cantcreate,dmname) FLUSH;
  330. X        sig_catcher(0);
  331. X    }
  332. X    }
  333. X#ifdef USETHREADS
  334. X    /* Keep selected_count accurate */
  335. X    if (!ctl_read(artnum)) {
  336. X    find_article(artnum);
  337. X    if (p_art) {
  338. X        if (selected_roots[p_art->root])
  339. X        selected_count--;
  340. X    } else
  341. X        unthreaded--;
  342. X    }
  343. X#endif
  344. X    oneless(artnum);            /* set the correct bit */
  345. X    dmcount++;
  346. X    fprintf(dmfp,"%ld\n",(long)artnum);
  347. X}
  348. X#endif
  349. X
  350. X/* mark article as read.  If article is cross referenced to other */
  351. X/* newsgroups, mark them read there also. */
  352. X
  353. void
  354. mark_as_read()
  355. X{
  356. X#ifdef USETHREADS
  357. X    find_article(art);
  358. X    /* Keep selected_count accurate */
  359. X    if (!ctl_read(art)) {
  360. X    if (p_art) {
  361. X        if (selected_roots[p_art->root])
  362. X        selected_count--;
  363. X    } else
  364. X        unthreaded--;
  365. X    }
  366. X#endif
  367. X    oneless(art);            /* set the correct bit */
  368. X    checkcount++;            /* get more worried about crashes */
  369. X#ifdef USETHREADS
  370. X    if (olden_days > 1 || !p_art || (p_art->flags & HAS_XREFS))
  371. X#endif
  372. X    chase_xrefs(art,TRUE);
  373. X}
  374. X
  375. X/* make sure we have bits set correctly down to firstbit */
  376. X
  377. void
  378. check_first(min)
  379. ART_NUM min;
  380. X{
  381. X    register ART_NUM i = firstbit;
  382. X
  383. X    if (min < absfirst)
  384. X    min = absfirst;
  385. X    if (min < i) {
  386. X    for (i--; i>=min; i--)
  387. X        ctl_set(i);        /* mark as read */
  388. X    firstart = firstbit = min;
  389. X    }
  390. X}
  391. X
  392. X/* bring back articles marked with M */
  393. X
  394. X#ifdef DELAYMARK
  395. void
  396. yankback()
  397. X{
  398. X    if (dmfp) {            /* delayed unmarks pending? */
  399. X#ifdef VERBOSE
  400. X    printf("\nReturning %ld Marked article%s...\n",(long)dmcount,
  401. X        dmcount == 1 ? nullstr : "s") FLUSH;
  402. X#endif
  403. X    rewind(dmfp);
  404. X    while (fgets(buf,sizeof buf,dmfp) != Nullch) {
  405. X        art = (ART_NUM)atol(buf);
  406. X        unmark_as_read();
  407. X    }
  408. X    fclose(dmfp);
  409. X    dmfp = Nullfp;
  410. X    UNLINK(dmname);        /* and be tidy */
  411. X    }
  412. X    dmcount = 0;
  413. X}
  414. X#endif
  415. X    
  416. X/* run down xref list and mark as read or unread */
  417. X
  418. int
  419. chase_xrefs(artnum,markread)
  420. ART_NUM artnum;
  421. int markread;
  422. X{
  423. X#ifdef ASYNC_PARSE
  424. X    if (parse_maybe(artnum))        /* make sure we have right header */
  425. X    return -1;
  426. X#endif
  427. X#ifdef DBM
  428. X    {
  429. X    datum lhs, rhs;
  430. X    datum fetch();
  431. X    register char *idp;
  432. X    char *ident_buf;
  433. X    static FILE * hist_file = Nullfp;
  434. X#else
  435. X    if (
  436. X#ifdef DEBUGGING
  437. X    debug & DEB_FEED_XREF ||
  438. X#endif
  439. X    htype[XREF_LINE].ht_minpos >= 0) {
  440. X                    /* are there article# xrefs? */
  441. X#endif /* DBM */
  442. X    char *xref_buf, *curxref;
  443. X    register char *xartnum;
  444. X    char *rver_buf = Nullch;
  445. X    static char *inews_site = Nullch;
  446. X    register ART_NUM x;
  447. X    char tmpbuf[128];
  448. X#ifdef DBM
  449. X    long pos;
  450. X    rver_buf = fetchlines(artnum,NGS_LINE);
  451. X                    /* get Newsgroups */
  452. X#ifdef XREF_WITH_COMMAS
  453. X    if (!index(rver_buf,','))    /* if no comma, no Xref! */
  454. X        return 0;
  455. X#endif
  456. X    if (hist_file == Nullfp) {    /* Init. file accesses */
  457. X#ifdef DEBUGGING
  458. X        if (debug)
  459. X        printf ("chase_xref: opening files\n");
  460. X#endif
  461. X        dbminit(filexp(ARTFILE));
  462. X        if ((hist_file = fopen (filexp(ARTFILE), "r")) == Nullfp)
  463. X        return 0;
  464. X    }
  465. X    xref_buf = safemalloc((MEM_SIZE)BUFSIZ);
  466. X    ident_buf = fetchlines(artnum,MESSID_LINE);
  467. X                    /* get Message-ID */
  468. X#ifdef DEBUGGING
  469. X    if (debug)
  470. X        printf ("chase_xref: Message-ID: %s\n", ident_buf);
  471. X#endif
  472. X    idp = ident_buf;
  473. X    while (*++idp)            /* make message-id case insensitive */
  474. X        if (isupper(*idp))
  475. X            *idp = tolower (*idp);
  476. X    lhs.dptr = ident_buf;        /* look up article by id */
  477. X    lhs.dsize = strlen(lhs.dptr) + 1;
  478. X    rhs = fetch(lhs);        /* fetch the record */
  479. X    if (rhs.dptr == NULL)        /* if null, nothing there */
  480. X        goto wild_goose;
  481. X    bcopy((void *)rhs.dptr,(void *)&pos, 4);
  482. X    fseek (hist_file, pos, 0);
  483. X                /* datum returned is position in hist file */
  484. X    fgets (xref_buf, BUFSIZ, hist_file);
  485. X#ifdef DEBUGGING
  486. X    if (debug)
  487. X        printf ("Xref from history: %s\n", xref_buf);
  488. X#endif
  489. X    curxref = cpytill(tmpbuf, xref_buf, '\t') + 1;
  490. X    curxref = cpytill(tmpbuf, curxref, '\t') + 1;
  491. X#ifdef DEBUGGING
  492. X    if (debug)
  493. X        printf ("chase_xref: curxref: %s\n", curxref);
  494. X#endif
  495. X#else /* !DBM */
  496. X#ifdef DEBUGGING
  497. X    if (htype[XREF_LINE].ht_minpos >= 0)
  498. X#endif
  499. X        xref_buf = fetchlines(artnum,XREF_LINE);
  500. X                    /* get xrefs list */
  501. X#ifdef DEBUGGING
  502. X    else {
  503. X        xref_buf = safemalloc((MEM_SIZE)100);
  504. X        printf("Give Xref: ") FLUSH;
  505. X        gets(xref_buf);
  506. X    }
  507. X#endif
  508. X#ifdef DEBUGGING
  509. X    if (debug & DEB_XREF_MARKER)
  510. X        printf("Xref: %s\n",xref_buf) FLUSH;
  511. X#endif
  512. X    curxref = cpytill(tmpbuf,xref_buf,' ') + 1;
  513. X
  514. X    /* Make sure site name on Xref matches what inews thinks site is.
  515. X     * Check first against last inews_site.  If it matches, fine.
  516. X     * If not, fetch inews_site from current Relay-Version line and
  517. X     * check again.  This is so that if the new administrator decides
  518. X     * to change the system name as known to inews, rn will still do
  519. X     * Xrefs correctly--each article need only match itself to be valid.
  520. X     */ 
  521. X    if (inews_site == Nullch || strNE(tmpbuf,inews_site)) {
  522. X#ifndef NORELAY
  523. X        char *t;
  524. X#endif
  525. X        if (inews_site != Nullch)
  526. X        free(inews_site);
  527. X#ifndef NORELAY
  528. X        rver_buf = fetchlines(artnum,RVER_LINE);
  529. X        if ((t = instr(rver_buf,"; site "), TRUE) == Nullch)
  530. X#else /* NORELAY */
  531. X          /* In version 2.10.3 of news or afterwards, the Relay-Version
  532. X           * and Posting-Version header lines have been removed.  For
  533. X           * the code below to work as intended, I have modified it to
  534. X           * extract the first component of the Path header line.  This
  535. X           * should give the same effect as did the old code with respect
  536. X           * to the use of the Relay-Version site name.
  537. X           */
  538. X          rver_buf = fetchlines(artnum,PATH_LINE);
  539. X          if (instr(rver_buf,"!", TRUE) == Nullch)
  540. X#endif /* NORELAY */
  541. X        inews_site = savestr(nullstr);
  542. X        else {
  543. X        char new_site[128];
  544. X
  545. X#ifndef NORELAY
  546. X        cpytill(new_site,t + 7,'.');
  547. X#else /* NORELAY */
  548. X              cpytill(new_site,rver_buf,'!');
  549. X#endif /* NORELAY */
  550. X        inews_site = savestr(new_site);
  551. X        }
  552. X        if (strNE(tmpbuf,inews_site)) {
  553. X#ifdef DEBUGGING
  554. X        if (debug)
  555. X            printf("Xref not from %s--ignoring\n",inews_site) FLUSH;
  556. X#endif
  557. X        goto wild_goose;
  558. X        }
  559. X    }
  560. X#endif /* DBM */
  561. X    while (*curxref) {
  562. X                    /* for each newsgroup */
  563. X        curxref = cpytill(tmpbuf,curxref,' ');
  564. X#ifdef DBM
  565. X        xartnum = index(tmpbuf,'/');
  566. X#else
  567. X        xartnum = index(tmpbuf,':');
  568. X#endif /* DBM */
  569. X        if (!xartnum)        /* probably an old-style Xref */
  570. X        break;
  571. X        *xartnum++ = '\0';
  572. X        if (strNE(tmpbuf,ngname)) {/* not the current newsgroup? */
  573. X        x = atol(xartnum);
  574. X        if (x)
  575. X            if (markread) {
  576. X            if (addartnum(x,tmpbuf))
  577. X                goto wild_goose;
  578. X            }
  579. X#ifdef MCHASE
  580. X            else
  581. X            subartnum(x,tmpbuf);
  582. X#endif
  583. X        }
  584. X        while (*curxref && isspace(*curxref))
  585. X        curxref++;
  586. X    }
  587. X      wild_goose:
  588. X    free(xref_buf);
  589. X#ifdef DBM
  590. X    free(ident_buf);
  591. X#endif /* DBM */
  592. X    if (rver_buf != Nullch)
  593. X        free(rver_buf);
  594. X    }
  595. X    return 0;
  596. X}
  597. X
  598. int
  599. initctl()
  600. X{
  601. X    char *mybuf = buf;            /* place to decode rc line */
  602. X    register char *s, *c, *h;
  603. X    register long i;
  604. X    register ART_NUM unread;
  605. X    
  606. X#ifdef DELAYMARK
  607. X    dmcount = 0;
  608. X#endif
  609. X    if ((lastart = getngsize(ng)) < 0)    /* this cannot happen (laugh here) */
  610. X    return -1;
  611. X
  612. X    absfirst = getabsfirst(ng,lastart);    /* remember first existing article */
  613. X    if (!absfirst)            /* no articles at all? */
  614. X    absfirst = 1;            /* pretend there is one */
  615. X#ifndef lint
  616. X    ctlsize = (MEM_SIZE)(OFFSET(lastart)/BITSPERBYTE+20);
  617. X#endif /* lint */
  618. X    ctlarea = safemalloc(ctlsize);    /* allocate control area */
  619. X
  620. X    /* now modify ctlarea to reflect what has already been read */
  621. X
  622. X    for (s = rcline[ng] + rcnums[ng]; *s == ' '; s++) ;
  623. X                    /* find numbers in rc line */
  624. X    i = strlen(s);
  625. X#ifndef lint
  626. X    if (i >= LBUFLEN-2)            /* bigger than buf? */
  627. X    mybuf = safemalloc((MEM_SIZE)(i+2));
  628. X#endif /* lint */
  629. X    strcpy(mybuf,s);            /* make scratch copy of line */
  630. X    mybuf[i++] = ',';            /* put extra comma on the end */
  631. X    mybuf[i] = '\0';
  632. X    s = mybuf;                /* initialize the for loop below */
  633. X#ifdef USETHREADS
  634. X    if (strnEQ(s,"1-",2)
  635. X     || strnEQ(s,"0-",2)) {        /* can we save some time here? */
  636. X#else
  637. X    if (strnEQ(s,"1-",2)) {        /* can we save some time here? */
  638. X#endif
  639. X    firstbit = atol(s+2)+1;        /* ignore first range thusly */
  640. X    s=index(s,',') + 1;
  641. X    }
  642. X    else
  643. X    firstbit = 1;            /* all the bits are valid for now */
  644. X    if (absfirst > firstbit) {        /* do we know already? */
  645. X    firstbit = absfirst;        /* no point calling getngmin again */
  646. X    }
  647. X    else if (artopen(firstbit) == Nullfp) {
  648. X                    /* first unread article missing? */
  649. X    i = getngmin(".",firstbit);    /* see if expire has been busy */
  650. X    if (i) {            /* avoid a bunch of extra opens */
  651. X        firstbit = i;
  652. X    }
  653. X    }
  654. X    firstart = firstbit;        /* firstart > firstbit in KILL */
  655. X#ifdef PENDING
  656. X#   ifdef CACHESUBJ
  657. X    subj_to_get = firstbit;
  658. X#   endif
  659. X#endif
  660. X    unread = lastart - firstbit + 1;    /* assume this range unread */
  661. X    for (i=OFFSET(firstbit)/BITSPERBYTE; i<ctlsize; i++)
  662. X    ctlarea[i] = 0;            /* assume unread */
  663. X#ifdef DEBUGGING
  664. X    if (debug & DEB_CTLAREA_BITMAP) {
  665. X    printf("\n%s\n",mybuf) FLUSH;
  666. X    for (i=1; i <= lastart; i++)
  667. X        if (! was_read(i))
  668. X        printf("%ld ",(long)i) FLUSH;
  669. X    }
  670. X#endif
  671. X    for ( ; (c = index(s,',')) != Nullch; s = ++c) {
  672. X                    /* for each range */
  673. X    ART_NUM min, max;
  674. X
  675. X    *c = '\0';            /* do not let index see past comma */
  676. X    if ((h = index(s,'-')) != Nullch) {    /* is there a -? */
  677. X        min = atol(s);
  678. X        max = atol(h+1);
  679. X        if (min < firstbit)        /* make sure range is in range */
  680. X        min = firstbit;
  681. X        if (max > lastart)
  682. X        max = lastart;
  683. X        if (min <= max)        /* non-null range? */
  684. X        unread -= max - min + 1;/* adjust unread count */
  685. X        for (i=min; i<=max; i++)    /* for all articles in range */
  686. X        ctl_set(i);        /* mark them read */
  687. X    }
  688. X    else if ((i = atol(s)) >= firstbit && i <= lastart) {
  689. X                    /* is single number reasonable? */
  690. X        ctl_set(i);            /* mark it read */
  691. X        unread--;            /* decrement articles to read */
  692. X    }
  693. X#ifdef DEBUGGING
  694. X    if (debug & DEB_CTLAREA_BITMAP) {
  695. X        printf("\n%s\n",s) FLUSH;
  696. X        for (i=1; i <= lastart; i++)
  697. X        if (! was_read(i))
  698. X            printf("%ld ",(long)i) FLUSH;
  699. X    }
  700. X#endif
  701. X    }
  702. X#ifdef DEBUGGING
  703. X    if (debug & DEB_CTLAREA_BITMAP) {
  704. X    fputs("\n(hit CR)",stdout) FLUSH;
  705. X    gets(cmd_buf);
  706. X    }
  707. X#endif
  708. X    if (mybuf != buf)
  709. X    free(mybuf);
  710. X    toread[ng] = unread;
  711. X    return 0;
  712. X}
  713. X
  714. void
  715. grow_ctl(newlast)
  716. ART_NUM newlast;
  717. X{
  718. X    ART_NUM tmpfirst;
  719. X    MEM_SIZE newsize;
  720. X    register ART_NUM i;
  721. X
  722. X    forcegrow = FALSE;
  723. X    if (newlast > lastart) {
  724. X    ART_NUM tmpart = art;
  725. X#ifndef lint
  726. X    newsize = (MEM_SIZE)(OFFSET(newlast)/BITSPERBYTE+2);
  727. X#else
  728. X    newsize = Null(MEM_SIZE);
  729. X#endif /* lint */
  730. X    if (newsize > ctlsize) {
  731. X        newsize += 20;
  732. X        ctlarea = saferealloc(ctlarea,newsize);
  733. X        ctlsize = newsize;
  734. X    }
  735. X    toread[ng] += (ART_UNREAD)(newlast-lastart);
  736. X    for (i=lastart+1; i<=newlast; i++)
  737. X        ctl_clear(i);    /* these articles are unread */
  738. X#ifdef CACHESUBJ
  739. X    if (subj_list != Null(char**)) {
  740. X#ifndef lint
  741. X        subj_list = (char**)saferealloc((char*)subj_list,
  742. X          (MEM_SIZE)((OFFSET(newlast)+2)*sizeof(char *)) );
  743. X#endif /* lint */
  744. X        for (i=lastart+1; i<=newlast; i++)
  745. X        subj_list[OFFSET(i)] = Nullch;
  746. X    }
  747. X#endif
  748. X    tmpfirst = lastart+1;
  749. X    lastart = newlast;
  750. X#ifdef KILLFILES
  751. X#ifdef VERBOSE
  752. X    IF(verbose)
  753. X        sprintf(buf,
  754. X        "%ld more article%s arrived--looking for more to kill...\n\n",
  755. X        (long)(lastart - tmpfirst + 1),
  756. X        (lastart > tmpfirst ? "s have" : " has" ) );
  757. X    ELSE            /* my, my, how clever we are */
  758. X#endif
  759. X#ifdef TERSE
  760. X        strcpy(buf, "More news--killing...\n\n");
  761. X#endif
  762. X    kill_unwanted(tmpfirst,buf,TRUE);
  763. X#endif
  764. X    art = tmpart;
  765. X    }
  766. X}
  767. X
  768. END_OF_FILE
  769. if test 18289 -ne `wc -c <'bits.c'`; then
  770.     echo shar: \"'bits.c'\" unpacked with wrong size!
  771. fi
  772. # end of 'bits.c'
  773. fi
  774. if test -f 'respond.c' -a "${1}" != "-c" ; then 
  775.   echo shar: Will not clobber existing file \"'respond.c'\"
  776. else
  777. echo shar: Extracting \"'respond.c'\" \(19717 characters\)
  778. sed "s/^X//" >'respond.c' <<'END_OF_FILE'
  779. X/* $Id: respond.c,v 4.4.2.1 1991/12/01 18:05:42 sob PATCH_2 sob $
  780. X *
  781. X * $Log: respond.c,v $
  782. X * Revision 4.4.2.1  1991/12/01  18:05:42  sob
  783. X * Patchlevel 2 changes
  784. X *
  785. X * Revision 4.4.1.1  1991/09/25  19:38:08  sob
  786. X * Some adaptions for CNEWS
  787. X *
  788. X * Revision 4.4  1991/09/09  20:27:37  sob
  789. X * release 4.4
  790. X *
  791. X *
  792. X * 
  793. X */
  794. X/* This software is Copyright 1991 by Stan Barber. 
  795. X *
  796. X * Permission is hereby granted to copy, reproduce, redistribute or otherwise
  797. X * use this software as long as: there is no monetary profit gained
  798. X * specifically from the use or reproduction or this software, it is not
  799. X * sold, rented, traded or otherwise marketed, and this copyright notice is
  800. X * included prominently in any copy made. 
  801. X *
  802. X * The author make no claims as to the fitness or correctness of this software
  803. X * for any use whatsoever, and it is provided as is. Any use of this software
  804. X * is at the user's own risk. 
  805. X */
  806. X
  807. X#include "EXTERN.h"
  808. X#include "common.h"
  809. X#include "intrp.h"
  810. X#include "head.h"
  811. X#include "term.h"
  812. X#include "ng.h"
  813. X#include "util.h"
  814. X#include "rn.h"
  815. X#include "artio.h"
  816. X#include "final.h"
  817. X#include "bits.h"
  818. X#include "decode.h"
  819. X#include "INTERN.h"
  820. X#include "respond.h"
  821. X
  822. static char nullart[] = "\nNull article\n";
  823. X
  824. void
  825. respond_init()
  826. X{
  827. X    ;
  828. X}
  829. X
  830. int
  831. save_article()
  832. X{
  833. X    bool use_pref, cut_line();
  834. X    register char *s, *c;
  835. X    char altbuf[CBUFLEN];
  836. X    int iter;
  837. X    bool interactive = (buf[1] == FINISHCMD);
  838. X    char cmd = *buf;
  839. X    
  840. X    if (!finish_command(interactive))    /* get rest of command */
  841. X    return SAVE_ABORT;
  842. X    if ((use_pref = isupper(cmd)) != 0)
  843. X    cmd = tolower(cmd);
  844. X#ifdef ASYNC_PARSE
  845. X    parse_maybe(art);
  846. X#endif
  847. X    savefrom = (cmd == 'w' || cmd == 'e' ? htype[PAST_HEADER].ht_minpos : 0);
  848. X    if (artopen(art) == Nullfp) {
  849. X#ifdef VERBOSE
  850. X    IF(verbose)
  851. X        fputs("\n\
  852. Saving null articles is not very productive!  :-)\n\
  853. X",stdout) FLUSH;
  854. X    ELSE
  855. X#endif
  856. X#ifdef TERSE
  857. X        fputs(nullart,stdout) FLUSH;
  858. X#endif
  859. X    return SAVE_DONE;
  860. X    }
  861. X    if (chdir(cwd)) {
  862. X    printf(nocd,cwd) FLUSH;
  863. X    sig_catcher(0);
  864. X    }
  865. X    if (cmd == 'e') {        /* is this an extract command? */
  866. X    static bool custom_extract = FALSE;
  867. X    int cnt = 0;
  868. X    bool found_cut = FALSE;
  869. X    char art_buf[LBUFLEN], *cmdstr;
  870. X
  871. X    s = buf+1;        /* skip e */
  872. X    while (*s == ' ') s++;    /* skip leading spaces */
  873. X    safecpy(altbuf,filexp(s),sizeof altbuf);
  874. X    s = altbuf;
  875. X    if (*s) {
  876. X        cmdstr = cpytill(buf,s,'|');    /* check for | */
  877. X        s = buf + strlen(buf)-1;
  878. X        while (*s == ' ') s--;        /* trim trailing spaces */
  879. X        *++s = '\0';
  880. X        if (*cmdstr) {
  881. X        s = cmdstr+1;            /* skip | */
  882. X        while (*s == ' ') s++;
  883. X        if (*s)    {            /* if new command, use it */
  884. X            if (extractprog)
  885. X            free(extractprog);
  886. X            extractprog = savestr(s);    /* put extracter in %e */
  887. X        }
  888. X        else
  889. X            cmdstr = extractprog;
  890. X        }
  891. X        else
  892. X        cmdstr = Nullch;
  893. X        s = buf;
  894. X    }
  895. X    else {
  896. X        if (extractdest)
  897. X        strcpy(s, extractdest);
  898. X        if (custom_extract)
  899. X        cmdstr = extractprog;
  900. X        else
  901. X        cmdstr = Nullch;
  902. X    }
  903. X    if (cmdstr) {
  904. X        if (strEQ(extractprog,"-"))
  905. X        cmdstr = Nullch;
  906. X        else if (decode_fp != Nullfp)
  907. X        decode_end();
  908. X    }
  909. X    custom_extract = (cmdstr != 0);
  910. X
  911. X    fseek(artfp,savefrom,0);
  912. X    if (*s != '/') {        /* relative path? */
  913. X        c = (s==buf ? altbuf : buf);
  914. X        interp(c, (sizeof buf), getval("SAVEDIR",SAVEDIR));
  915. X        if (makedir(c,MD_DIR))    /* ensure directory exists */
  916. X        strcpy(c,cwd);
  917. X        if (*s) {
  918. X        while (*c) c++;
  919. X        *c++ = '/';
  920. X        strcpy(c,s);        /* add filename */
  921. X        }
  922. X        s = (s==buf ? altbuf : buf);
  923. X    }
  924. X    if (*s != '/') {        /* path still relative? */
  925. X        c = (s==buf ? altbuf : buf);
  926. X        sprintf(c, "%s/%s", cwd, s);
  927. X        s = c;            /* absolutize it */
  928. X    }
  929. X    if (decode_fp != Nullfp) {
  930. X        printf("Continuing %s:%s\n", decode_fname,
  931. X        cmd != '\0' && strNE(extractdest,s) ?
  932. X         " (Ignoring conflicting directory)" : nullstr ) FLUSH;
  933. X        if (decode_type == UUDECODE)
  934. X        uudecode(artfp);
  935. X        else
  936. X        unship(artfp);
  937. X    }
  938. X    else {
  939. X        if (extractdest)
  940. X        free(extractdest);
  941. X        s = extractdest = savestr(s); /* make it handy for %E */
  942. X        if (makedir(s, MD_DIR)) {    /* ensure directory exists */
  943. X        int_count++;
  944. X        return SAVE_DONE;
  945. X        }
  946. X        if (chdir(s)) {
  947. X        printf(nocd,s) FLUSH;
  948. X        sig_catcher(0);
  949. X        }
  950. X        s = getwd(buf);        /* simplify path for output */
  951. X        while(fgets(art_buf,LBUFLEN,artfp) != Nullch) {
  952. X        if (*art_buf <= ' ')
  953. X            continue;    /* Ignore empty or initially-whitespace lines */
  954. X        if (found_cut && custom_extract) {
  955. X            printf("Extracting data into %s using %s:\n",
  956. X            s, extractprog) FLUSH;
  957. X            goto extract_it;
  958. X        }
  959. X        if (((*art_buf == '#' || *art_buf == ':')
  960. X          && (strnEQ(art_buf+1, "! /bin/sh", 9)
  961. X           || strnEQ(art_buf+1, "!/bin/sh", 8)
  962. X           || strnEQ(art_buf+2, "This is ", 8)))
  963. X         || strnEQ(art_buf, "sed ", 4)
  964. X         || strnEQ(art_buf, "cat ", 4)
  965. X         || strnEQ(art_buf, "echo ", 5)) {
  966. X            fseek(artfp,-(long)strlen(art_buf)-NL_SIZE+1,1);
  967. X            savefrom = ftell(artfp);
  968. X            if (custom_extract) {
  969. X            printf("Extracting shar into %s using %s:\n",
  970. X                s, extractprog) FLUSH;
  971. X            goto extract_it;
  972. X            }
  973. X            /* Check for special-case of shar'ed-uuencoded file */
  974. X            while(fgets(art_buf,LBUFLEN,artfp) != Nullch) {
  975. X            if (*art_buf == '#' || *art_buf == ':'
  976. X             || strnEQ(art_buf, "echo ", 5)
  977. X             || strnEQ(art_buf, "sed ", 4))
  978. X                continue;
  979. X            if (strnEQ(art_buf, "Xbegin ", 7)) {
  980. X                decode_type = UUDECODE;
  981. X                goto decode_it;
  982. X            }
  983. X            break;
  984. X            }
  985. X            printf("Extracting shar into %s:\n", s) FLUSH;
  986. X            if (extractprog)
  987. X            free(extractprog);
  988. X            extractprog = savestr(filexp(getval("UNSHAR",UNSHAR)));
  989. X          extract_it:
  990. X            cnt = 0;
  991. X            interp(cmd_buf,(sizeof cmd_buf),getval("EXSAVER",EXSAVER));
  992. X            termlib_reset();
  993. X            resetty();        /* restore tty state */
  994. X            doshell(SH,cmd_buf);
  995. X            noecho();        /* revert to cbreaking */
  996. X            crmode();
  997. X            termlib_init();
  998. X            break;
  999. X        }
  1000. X        else
  1001. X        if (!custom_extract
  1002. X         && (strEQ(art_buf,"$\n")
  1003. X          || strEQ(art_buf,"$ f\n"))) {
  1004. X            savefrom = ftell(artfp)-strlen(art_buf)-NL_SIZE+1;
  1005. X            if (found_cut
  1006. X             || (fgets(art_buf,LBUFLEN,artfp) != Nullch
  1007. X              && (strnEQ(art_buf, "ship ", 5)
  1008. X               || strnEQ(art_buf, "cont ", 5)))) {
  1009. X            decode_type = UNSHIP;
  1010. X            goto decode_it;
  1011. X            }
  1012. X        }
  1013. X        else
  1014. X        if (!custom_extract
  1015. X         && (strEQ(art_buf,"table\n")
  1016. X          || strnEQ(art_buf,"begin ", 6))) {
  1017. X            decode_type = UUDECODE;
  1018. X            savefrom = ftell(artfp)-strlen(art_buf)-NL_SIZE+1;
  1019. X         decode_it:
  1020. X            printf("Extracting %s file into %s:\n",
  1021. X            decode_type == UNSHIP? "shipped":"uuencoded", s) FLUSH;
  1022. X            if (extractprog)
  1023. X            free(extractprog);
  1024. X            extractprog = savestr("-");
  1025. X            fseek(artfp, savefrom, 0);
  1026. X            cnt = 0;
  1027. X            if (decode_type == UUDECODE) {
  1028. X            uud_start();
  1029. X            uudecode(artfp);
  1030. X            } else
  1031. X            unship(artfp);
  1032. X            break;
  1033. X        }
  1034. X        else {
  1035. X            if (cut_line(art_buf)) {
  1036. X            savefrom = ftell(artfp);
  1037. X            found_cut = TRUE;
  1038. X            }
  1039. X            else if (found_cut || ++cnt == 300) {
  1040. X            break;
  1041. X            }
  1042. X        }
  1043. X        }/* while */
  1044. X        if (cnt) {
  1045. X        if (custom_extract)
  1046. X            printf("Didn't find cut line for extraction to '%s'.\n",
  1047. X            extractprog) FLUSH;
  1048. X        else
  1049. X            printf("Unable to determine type of file.\n") FLUSH;
  1050. X        }
  1051. X    }/* if */
  1052. X    }
  1053. X    else if ((s = index(buf,'|')) != Nullch) {
  1054. X                /* is it a pipe command? */
  1055. X    s++;            /* skip the | */
  1056. X    while (*s == ' ') s++;
  1057. X    safecpy(altbuf,filexp(s),sizeof altbuf);
  1058. X    if (savedest)
  1059. X        free(savedest);
  1060. X    savedest = savestr(altbuf);
  1061. X    interp(cmd_buf, (sizeof cmd_buf), getval("PIPESAVER",PIPESAVER));
  1062. X                /* then set up for command */
  1063. X    termlib_reset();
  1064. X    resetty();        /* restore tty state */
  1065. X    if (use_pref)        /* use preferred shell? */
  1066. X        doshell(Nullch,cmd_buf);
  1067. X                /* do command with it */
  1068. X    else
  1069. X        doshell(sh,cmd_buf);    /* do command with sh */
  1070. X    noecho();        /* and stop echoing */
  1071. X    crmode();        /* and start cbreaking */
  1072. X    termlib_init();
  1073. X    }
  1074. X    else {            /* normal save */
  1075. X    bool there, mailbox;
  1076. X    char *savename = getval("SAVENAME",SAVENAME);
  1077. X
  1078. X    s = buf+1;        /* skip s or S */
  1079. X    if (*s == '-') {    /* if they are confused, skip - also */
  1080. X#ifdef VERBOSE
  1081. X        IF(verbose)
  1082. X        fputs("Warning: '-' ignored.  This isn't readnews.\n",stdout)
  1083. X          FLUSH;
  1084. X        ELSE
  1085. X#endif
  1086. X#ifdef TERSE
  1087. X        fputs("'-' ignored.\n",stdout) FLUSH;
  1088. X#endif
  1089. X        s++;
  1090. X    }
  1091. X    for (; *s == ' '; s++);    /* skip spaces */
  1092. X    safecpy(altbuf,filexp(s),sizeof altbuf);
  1093. X    s = altbuf;
  1094. X    if (! index(s,'/')) {
  1095. X        interp(buf, (sizeof buf), getval("SAVEDIR",SAVEDIR));
  1096. X        if (makedir(buf,MD_DIR))    /* ensure directory exists */
  1097. X        strcpy(buf,cwd);
  1098. X        if (*s) {
  1099. X        for (c = buf; *c; c++) ;
  1100. X        *c++ = '/';
  1101. X        strcpy(c,s);        /* add filename */
  1102. X        }
  1103. X        s = buf;
  1104. X    }
  1105. X    for (iter = 0;
  1106. X        (there = stat(s,&filestat) >= 0) &&
  1107. X        (filestat.st_mode & S_IFDIR);
  1108. X        iter++) {            /* is it a directory? */
  1109. X
  1110. X        c = (s+strlen(s));
  1111. X        *c++ = '/';            /* put a slash before filename */
  1112. X        interp(c, s==buf?(sizeof buf):(sizeof altbuf),
  1113. X        iter ? "News" : savename );
  1114. X                /* generate a default name somehow or other */
  1115. X    }
  1116. X    makedir(s,MD_FILE);
  1117. X    if (*s != '/') {        /* relative path? */
  1118. X        c = (s==buf ? altbuf : buf);
  1119. X        sprintf(c, "%s/%s", cwd, s);
  1120. X        s = c;            /* absolutize it */
  1121. X    }
  1122. X    if (savedest)
  1123. X        free(savedest);
  1124. X    s = savedest = savestr(s);    /* doesn't move any more */
  1125. X                    /* make it handy for %b */
  1126. X    if (!there) {
  1127. X        if (mbox_always)
  1128. X        mailbox = TRUE;
  1129. X        else if (norm_always)
  1130. X        mailbox = FALSE;
  1131. X        else {
  1132. X        char *dflt = (instr(savename,"%a", TRUE) ? "nyq" : "ynq");
  1133. X        
  1134. X        sprintf(cmd_buf,
  1135. X        "\nFile %s doesn't exist--\n    use mailbox format? [%s] ",
  1136. X          s,dflt);
  1137. X          reask_save:
  1138. X        in_char(cmd_buf, 'M');
  1139. X        putchar('\n') FLUSH;
  1140. X        setdef(buf,dflt);
  1141. X#ifdef VERIFY
  1142. X        printcmd();
  1143. X#endif
  1144. X        if (*buf == 'h') {
  1145. X#ifdef VERBOSE
  1146. X            IF(verbose)
  1147. X            printf("\n\
  1148. Type y to create %s as a mailbox.\n\
  1149. Type n to create it as a normal file.\n\
  1150. Type q to abort the save.\n\
  1151. X",s) FLUSH;
  1152. X            ELSE
  1153. X#endif
  1154. X#ifdef TERSE
  1155. X            fputs("\n\
  1156. y to create mailbox.\n\
  1157. n to create normal file.\n\
  1158. q to abort.\n\
  1159. X",stdout) FLUSH;
  1160. X#endif
  1161. X            goto reask_save;
  1162. X        }
  1163. X        else if (*buf == 'n') {
  1164. X            mailbox = FALSE;
  1165. X        }
  1166. X        else if (*buf == 'y') {
  1167. X            mailbox = TRUE;
  1168. X        }
  1169. X        else if (*buf == 'q') {
  1170. X            goto s_bomb;
  1171. X        }
  1172. X        else {
  1173. X            fputs(hforhelp,stdout) FLUSH;
  1174. X            settle_down();
  1175. X            goto reask_save;
  1176. X        }
  1177. X        }
  1178. X    }
  1179. X    else if (filestat.st_mode & S_IFCHR)
  1180. X        mailbox = FALSE;
  1181. X    else {
  1182. X        int tmpfd;
  1183. X        
  1184. X        tmpfd = open(s,0);
  1185. X        if (tmpfd == -1)
  1186. X        mailbox = FALSE;
  1187. X        else {
  1188. X        if (read(tmpfd,buf,LBUFLEN)) {
  1189. X            c = buf;
  1190. X            if (!isspace(MBOXCHAR))   /* if non-zero, */
  1191. X            while (isspace(*c))   /* check the first character */
  1192. X                c++;
  1193. X            mailbox = (*c == MBOXCHAR);
  1194. X        } else {
  1195. X            mailbox = mbox_always;    /* if zero length, recheck -M */
  1196. X        }
  1197. X        close(tmpfd);
  1198. X        }
  1199. X    }
  1200. X
  1201. X    safecpy(cmd_buf, filexp(mailbox ?
  1202. X        getval("MBOXSAVER",MBOXSAVER) :
  1203. X        getval("NORMSAVER",NORMSAVER) ), sizeof cmd_buf);
  1204. X                /* format the command */
  1205. X    termlib_reset();
  1206. X    resetty();        /* make terminal behave */
  1207. X    if (doshell(use_pref?Nullch:SH,cmd_buf)) {
  1208. X        termlib_init();
  1209. X        fputs("Not saved",stdout);
  1210. X    } else {
  1211. X        termlib_init();
  1212. X        printf("%s to %s %s",
  1213. X          there?"Appended":"Saved",
  1214. X          mailbox?"mailbox":"file",
  1215. X          s);
  1216. X    }
  1217. X    if (interactive)
  1218. X        putchar('\n') FLUSH;
  1219. X    noecho();        /* make terminal do what we want */
  1220. X    crmode();
  1221. X    }
  1222. s_bomb:
  1223. X#ifdef SERVER
  1224. X    if (chdir(spool)) {
  1225. X#else /* not SERVER */
  1226. X    if (chdir(spool) || chdir(ngdir)) {
  1227. X#endif /* SERVER */
  1228. X    printf(nocd,ngdir) FLUSH;
  1229. X    sig_catcher(0);
  1230. X    }
  1231. X    return SAVE_DONE;
  1232. X}
  1233. X
  1234. int
  1235. cancel_article()
  1236. X{
  1237. X    char *artid_buf;
  1238. X    char *ngs_buf;
  1239. X    char *from_buf;
  1240. X    char *reply_buf;
  1241. X    int myuid = getuid();
  1242. X    int r = -1;
  1243. X
  1244. X    if (artopen(art) == Nullfp) {
  1245. X#ifdef VERBOSE
  1246. X    IF(verbose)
  1247. X        fputs("\n\
  1248. Cancelling null articles is your idea of fun?  :-)\n\
  1249. X",stdout) FLUSH;
  1250. X    ELSE
  1251. X#endif
  1252. X#ifdef TERSE
  1253. X        fputs(nullart,stdout) FLUSH;
  1254. X#endif
  1255. X    return r;
  1256. X    }
  1257. X    reply_buf = fetchlines(art,REPLY_LINE);
  1258. X    from_buf = fetchlines(art,FROM_LINE);
  1259. X    artid_buf = fetchlines(art,ARTID_LINE);
  1260. X    ngs_buf = fetchlines(art,NGS_LINE);
  1261. X    if (!instr(from_buf,sitename,FALSE) ||
  1262. X    (!instr(from_buf,logname,TRUE) &&
  1263. X     !instr(reply_buf,logname,TRUE) &&
  1264. X#ifdef NEWSADMIN
  1265. X     myuid != newsuid &&
  1266. X#endif
  1267. X     myuid != ROOTID ) ) {
  1268. X#ifdef DEBUGGING
  1269. X        if (debug)
  1270. X        printf("\n%s@%s != %s\n",logname,sitename,from_buf) FLUSH;
  1271. X#endif
  1272. X#ifdef VERBOSE
  1273. X        IF(verbose)
  1274. X        fputs("\nYou can't cancel someone else's article\n",stdout)
  1275. X          FLUSH;
  1276. X        ELSE
  1277. X#endif
  1278. X#ifdef TERSE
  1279. X        fputs("\nNot your article\n",stdout) FLUSH;
  1280. X#endif
  1281. X    }
  1282. X    else {
  1283. X    tmpfp = fopen(headname,"w");    /* open header file */
  1284. X    if (tmpfp == Nullfp) {
  1285. X        printf(cantcreate,headname) FLUSH;
  1286. X        goto no_cancel;
  1287. X    }
  1288. X    interp(buf, (sizeof buf), getval("CANCELHEADER",CANCELHEADER));
  1289. X    fputs(buf,tmpfp);
  1290. X    fclose(tmpfp);
  1291. X    fputs("\nCanceling...\n",stdout) FLUSH;
  1292. X    r = doshell(sh,filexp(getval("CANCEL",CANCEL)));
  1293. X    }
  1294. no_cancel:
  1295. X    free(artid_buf);
  1296. X    free(ngs_buf);
  1297. X    free(from_buf);
  1298. X    free(reply_buf);
  1299. X    return r;
  1300. X}
  1301. X
  1302. int
  1303. supersede_article()        /* Supersedes: */
  1304. X{
  1305. X    char *artid_buf;
  1306. X    char *ngs_buf;
  1307. X    char *from_buf;
  1308. X    char *reply_buf;
  1309. X    int myuid = getuid();
  1310. X    int r = -1;
  1311. X
  1312. X    if (artopen(art) == Nullfp) {
  1313. X#ifdef VERBOSE
  1314. X    IF(verbose)
  1315. X        fputs("\n\
  1316. Superceding null articles is your idea of fun?  :-)\n\
  1317. X",stdout) FLUSH;
  1318. X    ELSE
  1319. X#endif
  1320. X#ifdef TERSE
  1321. X        fputs(nullart,stdout) FLUSH;
  1322. X#endif
  1323. X    return r;
  1324. X    }
  1325. X    reply_buf = fetchlines(art,REPLY_LINE);
  1326. X    from_buf = fetchlines(art,FROM_LINE);
  1327. X    artid_buf = fetchlines(art,ARTID_LINE);
  1328. X    ngs_buf = fetchlines(art,NGS_LINE);
  1329. X    if (!instr(from_buf,sitename,FALSE) ||
  1330. X    (!instr(from_buf,logname,TRUE) &&
  1331. X     !instr(reply_buf,logname,TRUE) &&
  1332. X#ifdef NEWSADMIN
  1333. X     myuid != newsuid &&
  1334. X#endif
  1335. X     myuid != ROOTID ) ) {
  1336. X#ifdef DEBUGGING
  1337. X        if (debug)
  1338. X        printf("\n%s@%s != %s\n",logname,sitename,from_buf) FLUSH;
  1339. X#endif
  1340. X#ifdef VERBOSE
  1341. X        IF(verbose)
  1342. X        fputs("\nYou can't supersede someone else's article\n",stdout)
  1343. X          FLUSH;
  1344. X        ELSE
  1345. X#endif
  1346. X#ifdef TERSE
  1347. X        fputs("\nNot your article\n",stdout) FLUSH;
  1348. X#endif
  1349. X    }
  1350. X    else {
  1351. X    tmpfp = fopen(headname,"w");    /* open header file */
  1352. X    if (tmpfp == Nullfp) {
  1353. X        printf(cantcreate,headname) FLUSH;
  1354. X        goto no_commute;
  1355. X    }
  1356. X    interp(buf, (sizeof buf), getval("SUPERSEDEHEADER",SUPERSEDEHEADER));
  1357. X    fputs(buf,tmpfp);
  1358. X    fclose(tmpfp);
  1359. X        safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),
  1360. X        sizeof cmd_buf);
  1361. X        invoke(cmd_buf,origdir);
  1362. X    r = 0;
  1363. X    }
  1364. no_commute:
  1365. X    free(artid_buf);
  1366. X    free(ngs_buf);
  1367. X    free(from_buf);
  1368. X    free(reply_buf);
  1369. X    return r;
  1370. X}
  1371. X
  1372. void
  1373. reply()
  1374. X{
  1375. X    bool incl_body = (*buf == 'R');
  1376. X    char *maildoer = savestr(filexp(getval("MAILPOSTER",MAILPOSTER)));
  1377. X
  1378. X    artopen(art);
  1379. X    tmpfp = fopen(headname,"w");    /* open header file */
  1380. X    if (tmpfp == Nullfp) {
  1381. X    printf(cantcreate,headname) FLUSH;
  1382. X    goto no_reply;
  1383. X    }
  1384. X    interp(buf, (sizeof buf), getval("MAILHEADER",MAILHEADER));
  1385. X    fputs(buf,tmpfp);
  1386. X    if (!instr(maildoer,"%h",TRUE))
  1387. X#ifdef VERBOSE
  1388. X    IF(verbose)
  1389. X        printf("\n%s\n(Above lines saved in file %s)\n",buf,headname)
  1390. X          FLUSH;
  1391. X    ELSE
  1392. X#endif
  1393. X#ifdef TERSE
  1394. X        printf("\n%s\n(Header in %s)\n",buf,headname) FLUSH;
  1395. X#endif
  1396. X    if (incl_body && artfp != Nullfp) {
  1397. X    interp(buf, (sizeof buf), getval("YOUSAID",YOUSAID));
  1398. X    fprintf(tmpfp,"%s\n",buf);
  1399. X#ifdef ASYNC_PARSE
  1400. X    parse_maybe(art);
  1401. X#endif
  1402. X    fseek(artfp,(long)htype[PAST_HEADER].ht_minpos,0);
  1403. X    while (fgets(buf,LBUFLEN,artfp) != Nullch) {
  1404. X        fprintf(tmpfp,"%s%s",indstr,buf);
  1405. X    }
  1406. X    fprintf(tmpfp,"\n");
  1407. X    }
  1408. X    fclose(tmpfp);
  1409. X    interp(cmd_buf, (sizeof cmd_buf), maildoer);
  1410. X    invoke(cmd_buf,origdir);
  1411. X    UNLINK(headname);        /* kill the header file */
  1412. no_reply:
  1413. X    free(maildoer);
  1414. X}
  1415. X
  1416. void
  1417. followup()
  1418. X{
  1419. X    bool incl_body = (*buf == 'F');
  1420. X    char hbuf[4*LBUFLEN];    /* four times the old size */
  1421. X    ART_NUM oldart = art;
  1422. X
  1423. X    if (!incl_body && art <= lastart) {
  1424. X    in_char("\n\nAre you starting an unrelated topic? [yn] ", 'F');
  1425. X    setdef(buf,"y");
  1426. X    if (*buf != 'n')
  1427. X        art = lastart + 1;
  1428. X    }
  1429. X    artopen(art);
  1430. X    tmpfp = fopen(headname,"w");
  1431. X    if (tmpfp == Nullfp) {
  1432. X    printf(cantcreate,headname) FLUSH;
  1433. X    art = oldart;
  1434. X    return;
  1435. X    }
  1436. X    interp(hbuf, (sizeof hbuf), getval("NEWSHEADER",NEWSHEADER));
  1437. X    fprintf(tmpfp,"%s",hbuf);
  1438. X    if (incl_body && artfp != Nullfp) {
  1439. X#ifdef VERBOSE
  1440. X    if (verbose)
  1441. X        fputs("\n\
  1442. X(Be sure to double-check the attribution against the signature, and\n\
  1443. trim the quoted article down as much as possible.)\n\
  1444. X",stdout) FLUSH;
  1445. X#endif
  1446. X    interp(buf, (sizeof buf), getval("ATTRIBUTION",ATTRIBUTION));
  1447. X    fprintf(tmpfp,"%s\n",buf);
  1448. X#ifdef ASYNC_PARSE
  1449. X    parse_maybe(art);
  1450. X#endif
  1451. X    fseek(artfp,(long)htype[PAST_HEADER].ht_minpos,0);
  1452. X    while (fgets(buf,LBUFLEN,artfp) != Nullch) {
  1453. X        fprintf(tmpfp,"%s%s",indstr,buf);
  1454. X    }
  1455. X    fprintf(tmpfp,"\n");
  1456. X    }
  1457. X    fclose(tmpfp);
  1458. X    safecpy(cmd_buf,filexp(getval("NEWSPOSTER",NEWSPOSTER)),sizeof cmd_buf);
  1459. X    invoke(cmd_buf,origdir);
  1460. X    UNLINK(headname);
  1461. X    art = oldart;
  1462. X}
  1463. X
  1464. void
  1465. invoke(cmd,dir)
  1466. char *cmd,*dir;
  1467. X{
  1468. X    if (chdir(dir)) {
  1469. X    printf(nocd,dir) FLUSH;
  1470. X    return;
  1471. X    }
  1472. X    termlib_reset();
  1473. X#ifdef VERBOSE
  1474. X    IF(verbose)
  1475. X    printf("\n(leaving cbreak mode; cwd=%s)\nInvoking command: %s\n\n",
  1476. X        dir,cmd) FLUSH;
  1477. X    ELSE
  1478. X#endif
  1479. X#ifdef TERSE
  1480. X    printf("\n(-cbreak; cwd=%s)\nInvoking: %s\n\n",dir,cmd) FLUSH;
  1481. X#endif
  1482. X    resetty();            /* make terminal well-behaved */
  1483. X    doshell(sh,cmd);        /* do the command */
  1484. X    noecho();            /* set no echo */
  1485. X    crmode();            /* and cbreak mode */
  1486. X#ifdef VERBOSE
  1487. X    IF(verbose)
  1488. X    fputs("\n(re-entering cbreak mode)\n",stdout) FLUSH;
  1489. X    ELSE
  1490. X#endif
  1491. X#ifdef TERSE
  1492. X    fputs("\n(+cbreak)\n",stdout) FLUSH;
  1493. X#endif
  1494. X    termlib_init();
  1495. X#ifdef SERVER
  1496. X    if (chdir(spool)) {
  1497. X#else /* not SERVER */
  1498. X    if (chdir(spool) || chdir(ngdir)) {
  1499. X#endif /* SERVER */
  1500. X    printf(nocd,ngdir) FLUSH;
  1501. X    sig_catcher(0);
  1502. X    }
  1503. X}
  1504. X
  1505. X/*
  1506. X** cut_line() determines if a line is meant as a "cut here" marker.
  1507. X** Some examples that we understand:
  1508. X**
  1509. X**  BEGIN--cut here--cut here
  1510. X**
  1511. X**  ------------------ tear at this line ------------------
  1512. X**
  1513. X**  #----cut here-----cut here-----cut here-----cut here----#
  1514. X*/
  1515. bool
  1516. cut_line(str)
  1517. char *str;
  1518. X{
  1519. X    char *cp, got_flag;
  1520. X    char word[80];
  1521. X    int  dash_cnt, equal_cnt, other_cnt;
  1522. X
  1523. X    /* Disallow any single-/double-quoted, parenthetical or c-commented
  1524. X    ** string lines.  Make sure it has the cut-phrase and at least six
  1525. X    ** '-'s or '='s.  If only four '-'s are present, check for a duplicate
  1526. X    ** of the cut phrase.  If over 20 unknown characters are encountered,
  1527. X    ** assume it isn't a cut line.  If we succeed, return TRUE.
  1528. X    */
  1529. X    for (cp = str, dash_cnt = equal_cnt = other_cnt = 0; *cp; cp++) {
  1530. X    switch (*cp) {
  1531. X    case '-':
  1532. X        dash_cnt++;
  1533. X        break;
  1534. X    case '=':
  1535. X        equal_cnt++;
  1536. X        break;
  1537. X    case '/':
  1538. X        if(*(cp+1) != '*') {
  1539. X        break;
  1540. X        }
  1541. X    case '"':
  1542. X    case '\'':
  1543. X    case '(':
  1544. X    case ')':
  1545. X    case '[':
  1546. X    case ']':
  1547. X    case '{':
  1548. X    case '}':
  1549. X        return FALSE;
  1550. X    default:
  1551. X        other_cnt++;
  1552. X        break;
  1553. X    }
  1554. X    }
  1555. X    if (dash_cnt < 4 && equal_cnt < 6)
  1556. X    return FALSE;
  1557. X
  1558. X    got_flag = 0;
  1559. X
  1560. X    for (*(cp = word) = '\0'; *str; str++) {
  1561. X    if (islower(*str))
  1562. X        *cp++ = *str;
  1563. X    else if (isupper(*str))
  1564. X        *cp++ = tolower(*str);
  1565. X    else {
  1566. X        if (*word) {
  1567. X        *cp = '\0';
  1568. X        switch (got_flag) {
  1569. X        case 2:
  1570. X            if (!strcmp(word, "line")
  1571. X             || !strcmp(word, "here"))
  1572. X            if ((other_cnt -= 4) <= 20)
  1573. X                return TRUE;
  1574. X            break;
  1575. X        case 1:
  1576. X            if (!strcmp(word, "this")) {
  1577. X            got_flag = 2;
  1578. X            other_cnt -= 4;
  1579. X            }
  1580. X            else if (!strcmp(word, "here")) {
  1581. X            other_cnt -= 4;
  1582. X            if ((dash_cnt >= 6 || equal_cnt >= 6)
  1583. X             && other_cnt <= 20)
  1584. X                return TRUE;
  1585. X            dash_cnt = 6;
  1586. X            got_flag = 0;
  1587. X            }
  1588. X            break;
  1589. X        case 0:
  1590. X            if (!strcmp(word, "cut")
  1591. X             || !strcmp(word, "snip")
  1592. X             || !strcmp(word, "tear")) {
  1593. X            got_flag = 1;
  1594. X            other_cnt -= strlen(word);
  1595. X            }
  1596. X            break;
  1597. X        }
  1598. X        *(cp = word) = '\0';
  1599. X        }
  1600. X    }
  1601. X    } /* for *str */
  1602. X
  1603. X    return FALSE;
  1604. X}
  1605. END_OF_FILE
  1606. if test 19717 -ne `wc -c <'respond.c'`; then
  1607.     echo shar: \"'respond.c'\" unpacked with wrong size!
  1608. fi
  1609. # end of 'respond.c'
  1610. fi
  1611. if test -f 'rt-rn.c' -a "${1}" != "-c" ; then 
  1612.   echo shar: Will not clobber existing file \"'rt-rn.c'\"
  1613. else
  1614. echo shar: Extracting \"'rt-rn.c'\" \(21936 characters\)
  1615. sed "s/^X//" >'rt-rn.c' <<'END_OF_FILE'
  1616. X/* $Id: rt-rn.c,v 4.4.3.1 1991/11/22 04:12:18 davison Trn $
  1617. X**
  1618. X** $Log: rt-rn.c,v $
  1619. X** Revision 4.4.3.1  1991/11/22  04:12:18  davison
  1620. X** Trn Release 2.0
  1621. X** 
  1622. X*/
  1623. X
  1624. X#include "EXTERN.h"
  1625. X#include "common.h"
  1626. X#include "term.h"
  1627. X#include "final.h"
  1628. X#include "util.h"
  1629. X#include "bits.h"
  1630. X#include "artio.h"
  1631. X#include "ng.h"
  1632. X#include "ngdata.h"
  1633. X#include "search.h"
  1634. X#include "artstate.h"
  1635. X#include "backpage.h"
  1636. X
  1637. X#ifdef USETHREADS
  1638. X
  1639. X#include "threads.h"
  1640. X#include "rthreads.h"
  1641. X
  1642. static void find_depth(), cache_tree(), display_tree();
  1643. static char letter();
  1644. X
  1645. X/* Find the article structure information based on article number.
  1646. X*/
  1647. void
  1648. find_article(artnum)
  1649. ART_NUM artnum;
  1650. X{
  1651. X    register PACKED_ARTICLE *article;
  1652. X    register int i;
  1653. X
  1654. X    if (!p_articles) {
  1655. X    p_art = Nullart;
  1656. X    return;
  1657. X    }
  1658. X
  1659. X    if (!p_art) {
  1660. X    p_art = p_articles;
  1661. X    }
  1662. X    /* Start looking for the article num from our last known spot in the array.
  1663. X    ** That way, if we already know where we are, we run into ourselves right
  1664. X    ** away.
  1665. X    */
  1666. X    for (article=p_art, i=p_art-p_articles; i < total.article; article++,i++) {
  1667. X    if (article->num == artnum) {
  1668. X        p_art = article;
  1669. X        return;
  1670. X    }
  1671. X    }
  1672. X    /* Didn't find it, so search the ones before our current position.
  1673. X    */
  1674. X    for (article = p_articles; article != p_art; article++) {
  1675. X    if (article->num == artnum) {
  1676. X        p_art = article;
  1677. X        return;
  1678. X    }
  1679. X    }
  1680. X    p_art = Nullart;
  1681. X}
  1682. X
  1683. static char tree_indent[] = {
  1684. X    ' ', 0,
  1685. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1686. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1687. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1688. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1689. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1690. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1691. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1692. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1693. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1694. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1695. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1696. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1697. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0,
  1698. X    ' ', ' ', ' ', ' ', 0,   ' ', ' ', ' ', ' ', 0
  1699. X};
  1700. X
  1701. char letters[] = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz?";
  1702. X
  1703. static PACKED_ARTICLE *tree_article;
  1704. X
  1705. static int max_depth, max_line = -1;
  1706. static int first_depth, first_line;
  1707. static int my_depth, my_line;
  1708. static bool node_on_line;
  1709. static int node_line_cnt;
  1710. X
  1711. static int line_num;
  1712. static int header_indent;
  1713. X
  1714. static char *tree_lines[11];
  1715. static char tree_buff[128], *str;
  1716. X
  1717. X/* Prepare tree display for inclusion in the article header.
  1718. X*/
  1719. void
  1720. init_tree()
  1721. X{
  1722. X    register PACKED_ARTICLE *article;
  1723. X
  1724. X    while (max_line >= 0) {        /* free any previous tree data */
  1725. X    free(tree_lines[max_line--]);
  1726. X    }
  1727. X    tree_article = curr_p_art;
  1728. X
  1729. X    if (!curr_p_art) {
  1730. X    return;
  1731. X    }
  1732. X    article = p_articles + p_roots[curr_p_art->root].articles;
  1733. X
  1734. X    max_depth = max_line = my_depth = my_line = node_line_cnt = 0;
  1735. X    find_depth(article, 0);
  1736. X
  1737. X    if (max_depth <= 5) {
  1738. X    first_depth = 0;
  1739. X    } else {
  1740. X    if (my_depth+2 > max_depth) {
  1741. X        first_depth = max_depth - 5;
  1742. X    } else if ((first_depth = my_depth - 3) < 0) {
  1743. X        first_depth = 0;
  1744. X    }
  1745. X    max_depth = first_depth + 5;
  1746. X    }
  1747. X    if (--max_line < max_tree_lines) {
  1748. X    first_line = 0;
  1749. X    } else {
  1750. X    if (my_line + max_tree_lines/2 > max_line) {
  1751. X        first_line = max_line - (max_tree_lines-1);
  1752. X    } else if ((first_line = my_line - (max_tree_lines-1)/2) < 0) {
  1753. X        first_line = 0;
  1754. X    }
  1755. X    max_line = first_line + max_tree_lines-1;
  1756. X    }
  1757. X
  1758. X    str = tree_buff;        /* initialize first line's data */
  1759. X    *str++ = ' ';
  1760. X    node_on_line = FALSE;
  1761. X    line_num = 0;
  1762. X    /* cache our portion of the tree */
  1763. X    cache_tree(article, 0, tree_indent);
  1764. X
  1765. X    max_depth = (max_depth-first_depth) * 5;    /* turn depth into char width */
  1766. X    max_line -= first_line;            /* turn max_line into count */
  1767. X    /* shorten tree if lower lines aren't visible */
  1768. X    if (node_line_cnt < max_line) {
  1769. X    max_line = node_line_cnt + 1;
  1770. X    }
  1771. X}
  1772. X
  1773. X/* A recursive routine to find the maximum tree extents and where we are.
  1774. X*/
  1775. static void
  1776. find_depth(article, depth)
  1777. PACKED_ARTICLE *article;
  1778. int depth;
  1779. X{
  1780. X    if (depth > max_depth) {
  1781. X    max_depth = depth;
  1782. X    }
  1783. X    for (;;) {
  1784. X    if (article == tree_article) {
  1785. X        my_depth = depth;
  1786. X        my_line = max_line;
  1787. X    }
  1788. X    if (article->child_cnt) {
  1789. X        find_depth(article+1, depth+1);
  1790. X    } else {
  1791. X        max_line++;
  1792. X    }
  1793. X    if (!article->siblings) {
  1794. X        break;
  1795. X    }
  1796. X    article += article->siblings;
  1797. X    }
  1798. X}
  1799. X
  1800. X/* Place the tree display in a maximum of 11 lines x 6 nodes.
  1801. X*/
  1802. static void
  1803. cache_tree(article, depth, cp)
  1804. PACKED_ARTICLE *article;
  1805. int depth;
  1806. char *cp;
  1807. X{
  1808. X    int depth_mode;
  1809. X
  1810. X    cp[1] = ' ';
  1811. X    if (depth >= first_depth && depth <= max_depth) {
  1812. X    cp += 5;
  1813. X    depth_mode = 1;
  1814. X    } else if (depth+1 == first_depth) {
  1815. X    depth_mode = 2;
  1816. X    } else {
  1817. X    cp = tree_indent;
  1818. X    depth_mode = 0;
  1819. X    }
  1820. X    for (;;) {
  1821. X    switch (depth_mode) {
  1822. X    case 1: {
  1823. X        char ch;
  1824. X
  1825. X        *str++ = (article->flags & ROOT_ARTICLE)? ' ' : '-';
  1826. X        if (article == tree_article) {
  1827. X        *str++ = '*';
  1828. X        }
  1829. X        if (was_read(article->num)) {
  1830. X        *str++ = '(';
  1831. X        ch = ')';
  1832. X        } else {
  1833. X        *str++ = '[';
  1834. X        ch = ']';
  1835. X        }
  1836. X        if (article == recent_p_art && article != tree_article) {
  1837. X        *str++ = '@';
  1838. X        }
  1839. X        *str++ = letter(article);
  1840. X        *str++ = ch;
  1841. X        if (article->child_cnt) {
  1842. X        *str++ = (article->child_cnt == 1)? '-' : '+';
  1843. X        }
  1844. X        if (article->siblings) {
  1845. X        *cp = '|';
  1846. X        } else {
  1847. X        *cp = ' ';
  1848. X        }
  1849. X        node_on_line = TRUE;
  1850. X        break;
  1851. X    }
  1852. X    case 2:
  1853. X        *tree_buff = (!article->child_cnt)? ' ' :
  1854. X        (article->child_cnt == 1)? '-' : '+';
  1855. X        break;
  1856. X    default:
  1857. X        break;
  1858. X    }
  1859. X    if (article->child_cnt) {
  1860. X        cache_tree(article+1, depth+1, cp);
  1861. X        cp[1] = '\0';
  1862. X    } else {
  1863. X        if (!node_on_line && first_line == line_num) {
  1864. X        first_line++;
  1865. X        }
  1866. X        if (line_num >= first_line) {
  1867. X        if (str[-1] == ' ') {
  1868. X            str--;
  1869. X        }
  1870. X        *str = '\0';
  1871. X        tree_lines[line_num-first_line]
  1872. X            = safemalloc(str-tree_buff + 1);
  1873. X        strcpy(tree_lines[line_num - first_line], tree_buff);
  1874. X        if (node_on_line) {
  1875. X            node_line_cnt = line_num - first_line;
  1876. X        }
  1877. X        }
  1878. X        line_num++;
  1879. X        node_on_line = FALSE;
  1880. X    }
  1881. X    if (!article->siblings || line_num > max_line) {
  1882. X        break;
  1883. X    }
  1884. X    article += article->siblings;
  1885. X    if (!article->siblings) {
  1886. X        *cp = '\\';
  1887. X    }
  1888. X    if (!first_depth) {
  1889. X        tree_indent[5] = ' ';
  1890. X    }
  1891. X    strcpy(tree_buff, tree_indent+5);
  1892. X    str = tree_buff + strlen(tree_buff);
  1893. X    }
  1894. X}
  1895. X
  1896. X/* Output a header line with possible tree display on the right hand side.
  1897. X** Does automatic wrapping of lines that are too long.
  1898. X*/
  1899. int
  1900. tree_puts(orig_line, header_line, use_underline)
  1901. char *orig_line;
  1902. ART_LINE header_line;
  1903. int use_underline;
  1904. X{
  1905. X    char *buf;
  1906. X    register char *line, *cp, *end;
  1907. X    int pad_cnt, wrap_at;
  1908. X    ART_LINE start_line = header_line;
  1909. X    int i;
  1910. X    char ch;
  1911. X
  1912. X    /* Make a modifiable copy of the line */
  1913. X    buf = safemalloc(strlen(orig_line) + 2);  /* yes, I mean "2" */
  1914. X    strcpy(buf, orig_line);
  1915. X    line = buf;
  1916. X
  1917. X    /* Change any embedded control characters to spaces */
  1918. X    for (end = line; *end && *end != '\n'; end++) {
  1919. X    if ((unsigned char)*end < ' ') {
  1920. X        *end = ' ';
  1921. X    }
  1922. X    }
  1923. X    *end = '\0';
  1924. X
  1925. X    if (!*line) {
  1926. X    strcpy(line, " ");
  1927. X    end = line+1;
  1928. X    }
  1929. X
  1930. X    /* If this is the first subject line, output it with a preceeding [1] */
  1931. X    if (use_underline && curr_p_art && (unsigned char)*line > ' ') {
  1932. X#ifdef NOFIREWORKS
  1933. X    no_sofire();
  1934. X#endif
  1935. X    standout();
  1936. X    putchar('[');
  1937. X    putchar(letter(curr_p_art));
  1938. X    putchar(']');
  1939. X    un_standout();
  1940. X    putchar(' ');
  1941. X    header_indent = 4;
  1942. X    line += 9;
  1943. X    i = 0;
  1944. X    } else {
  1945. X    if (*line != ' ') {
  1946. X        /* A "normal" header line -- output keyword and set header_indent
  1947. X        ** _except_ for the first line, which is a non-standard header.
  1948. X        */
  1949. X        if (!header_line || !(cp = index(line, ':')) || *++cp != ' ') {
  1950. X        header_indent = 0;
  1951. X        } else {
  1952. X        *cp = '\0';
  1953. X        fputs(line, stdout);
  1954. X        putchar(' ');
  1955. X        header_indent = ++cp - line;
  1956. X        line = cp;
  1957. X        }
  1958. X        i = 0;
  1959. X    } else {
  1960. X        /* Skip whitespace of continuation lines and prepare to indent */
  1961. X        while (*++line == ' ') {
  1962. X        ;
  1963. X        }
  1964. X        i = header_indent;
  1965. X    }
  1966. X    }
  1967. X    for (; *line; i = header_indent) {
  1968. X#ifdef CLEAREOL
  1969. X    maybe_eol();
  1970. X#endif
  1971. X    if (i) {
  1972. X        putchar('+');
  1973. X        while (--i) {
  1974. X        putchar(' ');
  1975. X        }
  1976. X    }
  1977. X    /* If no (more) tree lines, wrap at COLS-1 */
  1978. X    if (max_line < 0 || header_line > max_line+1) {
  1979. X        wrap_at = COLS-1;
  1980. X    } else {
  1981. X        wrap_at = COLS - max_depth - 5 - 3;
  1982. X    }
  1983. X    /* Figure padding between header and tree output, wrapping long lines */
  1984. X    pad_cnt = wrap_at - (end - line + header_indent);
  1985. X    if (pad_cnt <= 0) {
  1986. X        cp = line + wrap_at - header_indent - 1;
  1987. X        pad_cnt = 1;
  1988. X        while (cp > line && *cp != ' ') {
  1989. X        if (*--cp == ',' || *cp == '.' || *cp == '-' || *cp == '!') {
  1990. X            cp++;
  1991. X            break;
  1992. X        }
  1993. X        pad_cnt++;
  1994. X        }
  1995. X        if (cp == line) {
  1996. X        cp += wrap_at - header_indent;
  1997. X        pad_cnt = 0;
  1998. X        }
  1999. X        ch = *cp;
  2000. X        *cp = '\0';
  2001. X        /* keep rn's backpager happy */
  2002. X        vwtary(artline, vrdary(artline - 1));
  2003. X        artline++;
  2004. X    } else {
  2005. X        cp = end;
  2006. X        ch = '\0';
  2007. X    }
  2008. X    if (use_underline) {
  2009. X        underprint(line);
  2010. X    } else {
  2011. X        fputs(line, stdout);
  2012. X    }
  2013. X    *cp = ch;
  2014. X    /* Skip whitespace in wrapped line */
  2015. X    while (*cp == ' ') {
  2016. X        cp++;
  2017. X    }
  2018. X    line = cp;
  2019. X    /* Check if we've got any tree lines to output */
  2020. X    if (wrap_at != COLS-1 && header_line <= max_line) {
  2021. X        char *cp1, *cp2;
  2022. X
  2023. X        do {
  2024. X        putchar(' ');
  2025. X        } while (pad_cnt--);
  2026. X        /* Check string for the '*' flagging our current node
  2027. X        ** and the '@' flagging our prior node.
  2028. X        */
  2029. X        cp = tree_lines[header_line];
  2030. X        cp1 = index(cp, '*');
  2031. X        cp2 = index(cp, '@');
  2032. X        if (cp1 != Nullch) {
  2033. X        *cp1 = '\0';
  2034. X        }
  2035. X        if (cp2 != Nullch) {
  2036. X        *cp2 = '\0';
  2037. X        }
  2038. X        fputs(cp, stdout);
  2039. X        /* Handle standout output for '*' and '@' marked nodes, then
  2040. X        ** continue with the rest of the line.
  2041. X        */
  2042. X        while (cp1 || cp2) {
  2043. X        standout();
  2044. X        if (cp1 && (!cp2 || cp1 < cp2)) {
  2045. X            cp = cp1;
  2046. X            cp1 = Nullch;
  2047. X            *cp++ = '*';
  2048. X            putchar(*cp++);
  2049. X            putchar(*cp++);
  2050. X        } else {
  2051. X            cp = cp2;
  2052. X            cp2 = Nullch;
  2053. X            *cp++ = '@';
  2054. X        }
  2055. X        putchar(*cp++);
  2056. X        un_standout();
  2057. X        if (*cp) {
  2058. X            fputs(cp, stdout);
  2059. X        }
  2060. X        }/* while */
  2061. X    }/* if */
  2062. X    putchar('\n') FLUSH;
  2063. X    header_line++;
  2064. X    }/* for remainder of line */
  2065. X
  2066. X    /* free allocated copy of line */
  2067. X    free(buf);
  2068. X
  2069. X    /* return number of lines displayed */
  2070. X    return header_line - start_line;
  2071. X}
  2072. X
  2073. X/* Output any parts of the tree that are left to display.  Called at the
  2074. X** end of each header.
  2075. X*/
  2076. int
  2077. finish_tree(last_line)
  2078. ART_LINE last_line;
  2079. X{
  2080. X    ART_LINE start_line = last_line;
  2081. X
  2082. X    while (last_line <= max_line) {
  2083. X    artline++;
  2084. X    last_line += tree_puts("+", last_line, 0);
  2085. X    vwtary(artline, artpos);    /* keep rn's backpager happy */
  2086. X    }
  2087. X    return last_line - start_line;
  2088. X}
  2089. X
  2090. X/* Output the entire article tree for the user.
  2091. X*/
  2092. void
  2093. entire_tree()
  2094. X{
  2095. X    int j, root;
  2096. X
  2097. X    if (!ThreadedGroup) {
  2098. X    ThreadedGroup = use_data(TRUE);
  2099. X    find_article(art);
  2100. X    curr_p_art = p_art;
  2101. X    }
  2102. X    if (check_page_line()) {
  2103. X    return;
  2104. X    }
  2105. X    if (!p_art) {
  2106. X#ifdef VERBOSE
  2107. X    IF (verbose)
  2108. X        fputs("\nNo article tree to display.\n", stdout);
  2109. X    ELSE
  2110. X#endif
  2111. X#ifdef TERSE
  2112. X        fputs("\nNo tree.\n", stdout);
  2113. X#endif
  2114. X    } else {
  2115. X    root = p_art->root;
  2116. X#ifdef NOFIREWORKS
  2117. X    no_sofire();
  2118. X#endif
  2119. X    standout();
  2120. X    printf("T%ld:\n", (long)p_roots[root].root_num);
  2121. X    un_standout();
  2122. X    if (check_page_line()) {
  2123. X        return;
  2124. X    }
  2125. X    putchar('\n');
  2126. X    for (j = 0; j < p_roots[root].subject_cnt; j++) {
  2127. X        sprintf(buf, "[%c] %s\n", letters[j > 9+26+26 ? 9+26+26 : j],
  2128. X        subject_ptrs[root_subjects[root]+j]);
  2129. X        if (check_page_line()) {
  2130. X        return;
  2131. X        }
  2132. X        fputs(buf, stdout);
  2133. X    }
  2134. X    if (check_page_line()) {
  2135. X        return;
  2136. X    }
  2137. X    putchar('\n');
  2138. X    if (check_page_line()) {
  2139. X        return;
  2140. X    }
  2141. X    putchar(' ');
  2142. X    buf[3] = '\0';
  2143. X    display_tree(p_articles+p_roots[p_art->root].articles, tree_indent);
  2144. X
  2145. X    if (check_page_line()) {
  2146. X        return;
  2147. X    }
  2148. X    putchar('\n');
  2149. X    }
  2150. X}
  2151. X
  2152. X/* A recursive routine to output the entire article tree.
  2153. X*/
  2154. static void
  2155. display_tree(article, cp)
  2156. PACKED_ARTICLE *article;
  2157. char *cp;
  2158. X{
  2159. X    if (cp - tree_indent > COLS || page_line < 0) {
  2160. X    return;
  2161. X    }
  2162. X    cp[1] = ' ';
  2163. X    cp += 5;
  2164. X    for (;;) {
  2165. X    putchar((article->flags & ROOT_ARTICLE)? ' ' : '-');
  2166. X    if (was_read(article->num)) {
  2167. X        buf[0] = '(';
  2168. X        buf[2] = ')';
  2169. X    } else {
  2170. X        buf[0] = '[';
  2171. X        buf[2] = ']';
  2172. X    }
  2173. X    buf[1] = letter(article);
  2174. X    if (article == curr_p_art) {
  2175. X        standout();
  2176. X        fputs(buf, stdout);
  2177. X        un_standout();
  2178. X    } else if (article == recent_p_art) {
  2179. X        putchar(buf[0]);
  2180. X        standout();
  2181. X        putchar(buf[1]);
  2182. X        un_standout();
  2183. X        putchar(buf[2]);
  2184. X    } else {
  2185. X        fputs(buf, stdout);
  2186. X    }
  2187. X
  2188. X    if (article->siblings) {
  2189. X        *cp = '|';
  2190. X    } else {
  2191. X        *cp = ' ';
  2192. X    }
  2193. X    if (article->child_cnt) {
  2194. X        putchar((article->child_cnt == 1)? '-' : '+');
  2195. X        display_tree(article+1, cp);
  2196. X        cp[1] = '\0';
  2197. X    } else {
  2198. X        putchar('\n') FLUSH;
  2199. X    }
  2200. X    if (!article->siblings) {
  2201. X        break;
  2202. X    }
  2203. X    article += article->siblings;
  2204. X    if (!article->siblings) {
  2205. X        *cp = '\\';
  2206. X    }
  2207. X    tree_indent[5] = ' ';
  2208. X    if (check_page_line()) {
  2209. X        return;
  2210. X    }
  2211. X    fputs(tree_indent+5, stdout);
  2212. X    }
  2213. X}
  2214. X
  2215. int
  2216. check_page_line()
  2217. X{
  2218. X    if (page_line < 0) {
  2219. X    return -1;
  2220. X    }
  2221. X    if (page_line >= LINES || int_count) {
  2222. X      register int cmd = -1;
  2223. X    if (int_count || (cmd = get_anything())) {
  2224. X        page_line = -1;        /* disable further printing */
  2225. X        if (cmd > 0) {
  2226. X        pushchar(cmd);
  2227. X        }
  2228. X        return cmd;
  2229. X    }
  2230. X    }
  2231. X    page_line++;
  2232. X    return 0;
  2233. X}
  2234. X
  2235. X/* Calculate the subject letter representation.  "Place-holder" nodes
  2236. X** are marked with a ' ', others get a letter in the sequence:
  2237. X**    ' ', '1'-'9', 'A'-'Z', 'a'-'z', '?'
  2238. X*/
  2239. static char
  2240. letter(article)
  2241. PACKED_ARTICLE *article;
  2242. X{
  2243. X    register int subj = article->subject;
  2244. X
  2245. X    if (subj < 0) {
  2246. X    return ' ';
  2247. X    }
  2248. X    subj -= root_subjects[article->root];
  2249. X    if (subj < 9+26+26) {
  2250. X    return letters[subj];
  2251. X    }
  2252. X    return '?';
  2253. X}
  2254. X
  2255. X/* Find the first unread article in the (possibly selected) root order.
  2256. X*/
  2257. void
  2258. first_art()
  2259. X{
  2260. X    if (!ThreadedGroup) {
  2261. X    art = firstart;
  2262. X    return;
  2263. X    }
  2264. X    p_art = Nullart;
  2265. X    art = lastart+1;
  2266. X    follow_thread('n');
  2267. X}
  2268. X
  2269. X/* Perform a command over all or a section of the article tree.  Most of
  2270. X** the option letters match commands entered from article mode:
  2271. X**   n - find the next unread article after current article.
  2272. X**  ^N - find the next unread article with the same subject.
  2273. X**   N - goto the next article in the thread.
  2274. X**   j - junk the entire thread.
  2275. X**   J - junk the entire thread, chasing xrefs.
  2276. X**   k - junk all articles with this same subject, chasing xrefs.
  2277. X**   K - kill all this article's descendants, chasing xrefs (we know that
  2278. X**     the caller killed the current article on the way here).
  2279. X**   u - mark entire thread as "unread".
  2280. X**   U - mark this article and its descendants as "unread".
  2281. X**   x - go through the unread articles and just chase their xrefs.
  2282. X**   f - follow the thread (like 'n'), but don't attempt to find a new thread
  2283. X**     if we run off the end.
  2284. X*/
  2285. void
  2286. follow_thread(cmd)
  2287. char_int cmd;
  2288. X{
  2289. X    int curr_subj = -1, selected;
  2290. X    PACKED_ARTICLE *root_limit, *p_art_old = Nullart;
  2291. X    bool subthread_flag, chase_flag;
  2292. X
  2293. X    reread = FALSE;
  2294. X
  2295. X    if (cmd == 'N') {
  2296. X    reread = TRUE;
  2297. X    }
  2298. X    if (!p_art) {
  2299. X    if (ThreadedGroup && art > lastart) {
  2300. X        p_art = root_limit = p_articles;
  2301. X        goto follow_root;
  2302. X    }
  2303. X    art++;
  2304. X    return;
  2305. X    }
  2306. X    if (cmd == 'k' || cmd == Ctl('n')) {
  2307. X    if ((curr_subj = p_art->subject) == -1) {
  2308. X        return;
  2309. X    }
  2310. X    p_art_old = p_art;
  2311. X    }
  2312. X    selected = (selected_roots[p_art->root] & 1);
  2313. X    if (cmd == 'U' || cmd == 'K') {
  2314. X    subthread_flag = TRUE;
  2315. X    p_art_old = p_art;
  2316. X    } else {
  2317. X    subthread_flag = FALSE;
  2318. X    }
  2319. X    chase_flag = (!olden_days && (cmd == 'J' || cmd == 'k' || cmd == 'K'));
  2320. X
  2321. X    /* Some commands encompass the entire thread */
  2322. X    if (cmd == 'k' || cmd == 'j' || cmd == 'J' || cmd == 'u' || cmd == 'x') {
  2323. X    p_art = p_articles + p_roots[p_art->root].articles;
  2324. X    art = p_art->num;
  2325. X    }
  2326. X    /* The current article is already marked as read for 'K' */
  2327. X    if (cmd == 'k' || cmd == 'j' || cmd == 'J') {
  2328. X    if (!was_read(art) && (curr_subj < 0 || curr_subj == p_art->subject)) {
  2329. X        set_read(art, selected, chase_flag);
  2330. X    }
  2331. X    cmd = 'K';
  2332. X    }
  2333. X    if (cmd == 'u') {
  2334. X    p_art_old = p_art;
  2335. X    cmd = 'U';
  2336. X    }
  2337. X    if (cmd == 'U') {
  2338. X    if (p_art->subject != -1) {
  2339. X        set_unread(art, selected);
  2340. X    }
  2341. X    root_article_cnts[p_art->root] = 1;
  2342. X    scan_all_roots = FALSE;
  2343. X    }
  2344. X    if (cmd == 'x') {
  2345. X    if (olden_days) {
  2346. X        return;
  2347. X    }
  2348. X    if ((p_art->flags & HAS_XREFS) && !was_read(art)) {
  2349. X        chase_xrefs(art, TRUE);
  2350. X    }
  2351. X    }
  2352. X  follow_again:
  2353. X    selected = (selected_roots[p_art->root] & 1);
  2354. X    root_limit = upper_limit(p_art, subthread_flag);
  2355. X    for (;;) {
  2356. X    if (++p_art == root_limit) {
  2357. X        break;
  2358. X    }
  2359. X    if (!(art = p_art->num)) {
  2360. X        continue;
  2361. X    }
  2362. X    if (cmd == 'K' || p_art->subject == -1) {
  2363. X        if (!was_read(art)
  2364. X         && (curr_subj < 0 || curr_subj == p_art->subject)) {
  2365. X        set_read(art, selected, chase_flag);
  2366. X        }
  2367. X    } else if (cmd == 'U') {
  2368. X        set_unread(art, selected);
  2369. X    } else if (cmd == 'x') {
  2370. X        if ((p_art->flags & HAS_XREFS) && !was_read(art)) {
  2371. X        chase_xrefs(art, TRUE);
  2372. X        }
  2373. X    } else if (!was_read(art)
  2374. X        && (curr_subj < 0 || curr_subj == p_art->subject)) {
  2375. X        return;
  2376. X    } else if (cmd == 'N') {
  2377. X        return;
  2378. X    }
  2379. X    }/* for */
  2380. X    if (p_art_old) {
  2381. X    p_art = p_art_old;
  2382. X    if (cmd == 'U' && p_art->subject != -1) {
  2383. X        art = p_art->num;
  2384. X        return;
  2385. X    }
  2386. X    p_art_old = Nullart;
  2387. X    cmd = 'n';
  2388. X    curr_subj = -1;
  2389. X    subthread_flag = FALSE;
  2390. X    goto follow_again;
  2391. X    }
  2392. X    if (cmd == 'f') {
  2393. X    p_art = Nullart;
  2394. X    art = lastart+1;
  2395. X    return;
  2396. X    }
  2397. X  follow_root:
  2398. X    if (root_limit != p_articles + total.article) {
  2399. X    register int r;
  2400. X
  2401. X    for (r = p_art->root; r < total.root; r++) {
  2402. X        if (!selected_root_cnt || selected_roots[r]) {
  2403. X        p_art = p_articles + p_roots[r].articles;
  2404. X        art = p_art->num;
  2405. X        if (p_art->subject == -1 || (cmd != 'N' && was_read(art))) {
  2406. X            if (cmd != 'N') {
  2407. X            cmd = 'n';
  2408. X            }
  2409. X            curr_subj = -1;
  2410. X            subthread_flag = FALSE;
  2411. X            goto follow_again;
  2412. X        }
  2413. X        return;
  2414. X        }
  2415. X    }
  2416. X    }
  2417. X    if (!count_roots(FALSE) && unthreaded) {
  2418. X    /* No threaded articles left -- blow everything else away */
  2419. X    for (art = firstbit; art <= lastart; art++) {
  2420. X        oneless(art);
  2421. X    }
  2422. X    unthreaded = 0;
  2423. X    }
  2424. X    p_art = Nullart;
  2425. X    art = lastart+1;
  2426. X    if (cmd == 'N') {
  2427. X    forcelast = TRUE;
  2428. X    reread = FALSE;
  2429. X    }
  2430. X}
  2431. X
  2432. X/* Go backward in the article tree.  Options match commands in article mode:
  2433. X**    p - previous unread article.
  2434. X**   ^P - previous unread article with same subject.
  2435. X**    P - previous article.
  2436. X*/
  2437. void
  2438. backtrack_thread(cmd)
  2439. char_int cmd;
  2440. X{
  2441. X    int curr_subj = -1, selected;
  2442. X    PACKED_ARTICLE *root_limit, *p_art_old = Nullart;
  2443. X
  2444. X    if (art > lastart) {
  2445. X    p_art = p_articles + total.article - 1;
  2446. X    root_limit = Nullart;
  2447. X    goto backtrack_root;
  2448. X    }
  2449. X    if (!p_art) {
  2450. X    art--;
  2451. X    return;
  2452. X    }
  2453. X    if (cmd == Ctl('p')) {
  2454. X    if ((curr_subj = p_art->subject) == -1) {
  2455. X        return;
  2456. X    }
  2457. X    p_art_old = p_art;
  2458. X    }
  2459. X  backtrack_again:
  2460. X    selected = (selected_roots[p_art->root] & 1);
  2461. X    root_limit = p_articles + p_roots[p_art->root].articles;
  2462. X    for (;;) {
  2463. X    if (p_art-- == root_limit) {
  2464. X        break;
  2465. X    }
  2466. X    if (!(art = p_art->num)) {
  2467. X        continue;
  2468. X    }
  2469. X    if (p_art->subject == -1) {
  2470. X        set_read(art, selected, FALSE);
  2471. X    } else if (!was_read(art)
  2472. X        && (curr_subj < 0 || curr_subj == p_art->subject)) {
  2473. X        return;
  2474. X    } else if (cmd == 'P') {
  2475. X        reread = TRUE;
  2476. X        return;
  2477. X    }
  2478. X    }/* for */
  2479. X    if (p_art_old) {
  2480. X    p_art = p_art_old;
  2481. X    p_art_old = Nullart;
  2482. X    curr_subj = -1;
  2483. X    goto backtrack_again;
  2484. X    }
  2485. X  backtrack_root:
  2486. X    if (root_limit != p_articles) {
  2487. X    register int r;
  2488. X
  2489. X    for (r = p_art->root; r >= 0; r--) {
  2490. X        if (!selected_root_cnt || selected_roots[r]) {
  2491. X        art = p_art->num;
  2492. X        if (cmd != 'P' && was_read(art)) {
  2493. X            goto backtrack_again;
  2494. X        }
  2495. X        return;
  2496. X        }
  2497. X        p_art = p_articles + p_roots[r].articles - 1;
  2498. X    }
  2499. X    }
  2500. X    p_art = Nullart;
  2501. X    art = absfirst-1;
  2502. X}
  2503. X
  2504. X/* Find the next root (first if p_art == NULL).  If roots are selected,
  2505. X** only choose from selected roots.
  2506. X*/
  2507. void
  2508. next_root()
  2509. X{
  2510. X    register int r;
  2511. X
  2512. X    reread = FALSE;
  2513. X
  2514. X    if (p_art) {
  2515. X    r = p_art->root+1;
  2516. X    } else {
  2517. X    r = 0;
  2518. X    }
  2519. X    for (; r < total.root; r++) {
  2520. X    if (!selected_root_cnt || selected_roots[r]) {
  2521. X      try_again:
  2522. X        p_art = p_articles + p_roots[r].articles;
  2523. X        art = p_art->num;
  2524. X        if (p_art->subject == -1 || (!reread && was_read(art))) {
  2525. X        follow_thread(reread ? 'N' : 'f');
  2526. X        if (art == lastart+1) {
  2527. X            if (scan_all_roots || selected_root_cnt
  2528. X             || root_article_cnts[r]) {
  2529. X            reread = TRUE;
  2530. X            goto try_again;
  2531. X            }
  2532. X            continue;
  2533. X        }
  2534. X        }
  2535. X        return;
  2536. X    }
  2537. X    }
  2538. X    p_art = Nullart;
  2539. X    art = lastart+1;
  2540. X    forcelast = TRUE;
  2541. X}
  2542. X
  2543. X/* Find previous root (or last if p_art == NULL).  If roots are selected,
  2544. X** only choose from selected roots.
  2545. X*/
  2546. void
  2547. prev_root()
  2548. X{
  2549. X    register int r;
  2550. X
  2551. X    reread = FALSE;
  2552. X
  2553. X    if (p_art) {
  2554. X    r = p_art->root - 1;
  2555. X    } else {
  2556. X    r = total.root - 1;
  2557. X    }
  2558. X    for (; r >= 0; r--) {
  2559. X    if (!selected_root_cnt || selected_roots[r]) {
  2560. X      try_again:
  2561. X        p_art = p_articles + p_roots[r].articles;
  2562. X        art = p_art->num;
  2563. X        if (p_art->subject == -1 || (!reread && was_read(art))) {
  2564. X        follow_thread(reread ? 'N' : 'f');
  2565. X        if (art == lastart+1) {
  2566. X            if (scan_all_roots || selected_root_cnt
  2567. X             || root_article_cnts[r]) {
  2568. X            reread = TRUE;
  2569. X            goto try_again;
  2570. X            }
  2571. X            continue;
  2572. X        }
  2573. X        }
  2574. X        return;
  2575. X    }
  2576. X    }
  2577. X    p_art = Nullart;
  2578. X    art = lastart+1;
  2579. X    forcelast = TRUE;
  2580. X}
  2581. X
  2582. X/* Return a pointer value that we will equal when we've reached the end of
  2583. X** the current (sub-)thread.
  2584. X*/
  2585. PACKED_ARTICLE *
  2586. upper_limit(artp, subthread_flag)
  2587. PACKED_ARTICLE *artp;
  2588. bool_int subthread_flag;
  2589. X{
  2590. X    if (subthread_flag) {
  2591. X    for (;;) {
  2592. X        if (artp->siblings) {
  2593. X        return artp + artp->siblings;
  2594. X        }
  2595. X        if (!artp->parent) {
  2596. X        break;
  2597. X        }
  2598. X        artp += artp->parent;
  2599. X    }
  2600. X    }
  2601. X    return p_articles + (artp->root == total.root-1 ?
  2602. X    total.article : p_roots[artp->root+1].articles);
  2603. X}
  2604. X
  2605. X#endif /* USETHREADS */
  2606. END_OF_FILE
  2607. if test 21936 -ne `wc -c <'rt-rn.c'`; then
  2608.     echo shar: \"'rt-rn.c'\" unpacked with wrong size!
  2609. fi
  2610. # end of 'rt-rn.c'
  2611. fi
  2612. if test -f 'rt-select.c' -a "${1}" != "-c" ; then 
  2613.   echo shar: Will not clobber existing file \"'rt-select.c'\"
  2614. else
  2615. echo shar: Extracting \"'rt-select.c'\" \(23184 characters\)
  2616. sed "s/^X//" >'rt-select.c' <<'END_OF_FILE'
  2617. X/* $Id: rt-select.c,v 4.4.3.1 1991/11/22 04:12:18 davison Trn $
  2618. X**
  2619. X** $Log: rt-select.c,v $
  2620. X** Revision 4.4.3.1  1991/11/22  04:12:18  davison
  2621. X** Trn Release 2.0
  2622. X** 
  2623. X*/
  2624. X
  2625. X#include "EXTERN.h"
  2626. X#include "common.h"
  2627. X#include "rn.h"
  2628. X#include "rcstuff.h"
  2629. X#include "term.h"
  2630. X#include "final.h"
  2631. X#include "util.h"
  2632. X#include "help.h"
  2633. X#include "bits.h"
  2634. X#include "artsrch.h"
  2635. X#include "ng.h"
  2636. X#include "ngdata.h"
  2637. X#include "ngstuff.h"
  2638. X
  2639. X#ifdef USETHREADS
  2640. X
  2641. X#include "threads.h"
  2642. X#include "rthreads.h"
  2643. X
  2644. static int count_subj_lines();
  2645. static void display_subj();
  2646. X
  2647. X/* When display mode is 'l', each author gets a separate line; when 'm', up to
  2648. X** three authors share a line; when 's', no authors are displayed.
  2649. X*/
  2650. static char *display_mode = select_order;
  2651. static ART_NUM article_count;
  2652. static int author_line;
  2653. static char first_two_chars[3] = { ' ', ' ', '\0' }, mask = 1;
  2654. X
  2655. X#define MAX_SEL 64
  2656. X
  2657. X/* Display a menu of roots for the user to choose from.  If cmd is '+'
  2658. X** we display all the unread roots and allow the user to mark roots as
  2659. X** selected and perform various commands upon the articles.  If cmd is
  2660. X** 'U' we display all the previously read roots and allow the user to
  2661. X** select which ones should be marked as unread.
  2662. X*/
  2663. char
  2664. select_thread(cmd)
  2665. char_int cmd;
  2666. X{
  2667. X    register int i, j, cnt;
  2668. X    ART_NUM art_hold = art;
  2669. X    int line_cnt, screen_line, subj_line_cnt;
  2670. X    int cur_root, page_root, last_root = -1;
  2671. X    ART_LINE running_total, last_running;
  2672. X    int last_line, got_dash;
  2673. X    int max_root;
  2674. X    int first, last;
  2675. X    int root_line[MAX_SEL], root_hold[MAX_SEL];
  2676. X    int ch, action;
  2677. X    char page_char, end_char;
  2678. X    char promptbuf[80];
  2679. X    bool etc, clean_screen, empty_ok, displayed_status;
  2680. X    char oldmode = mode;
  2681. X#ifndef CONDSUB
  2682. X    char tmpbuf[2];
  2683. X#endif
  2684. X    char *select_chars, *in_select;
  2685. X    int max_cnt;
  2686. X
  2687. X    mode = 't';
  2688. X    unread_selector = (cmd == 'U');
  2689. X    clear_on_stop = TRUE;
  2690. X    empty_ok = FALSE;
  2691. X
  2692. X  select_threads:
  2693. X    /* Setup for selecting articles to read or set unread */
  2694. X    scan_all_roots = FALSE;
  2695. X    if (unread_selector) {
  2696. X    page_char = '>';
  2697. X    end_char = 'Z';
  2698. X    page_root = 0;
  2699. X    last_root = -1;
  2700. X    cmd = 0;
  2701. X    } else {
  2702. X    page_char = page_select;
  2703. X    end_char = end_select;
  2704. X    page_root = select_page;
  2705. X    if (curr_p_art) {
  2706. X        last_root = curr_p_art->root;
  2707. X    }
  2708. X    }
  2709. X    mask = unread_selector+1;
  2710. X
  2711. X    /* Leave empty roots selected for a short time to give them a chance
  2712. X    ** to Esc out of the selector if they got here by mistake.
  2713. X    */
  2714. X    max_root = count_roots(FALSE);
  2715. X
  2716. X    /* If nothing to display, we're done. */
  2717. X    if (!article_count && !empty_ok) {
  2718. X     all_empty:
  2719. X    clear_on_stop = FALSE;
  2720. X    mode = oldmode;
  2721. X    putchar('\n');
  2722. X    if (unread_selector) {
  2723. X#ifdef VERBOSE
  2724. X        IF (verbose)
  2725. X        fputs("\nNo articles to set unread.\n", stdout);
  2726. X        ELSE
  2727. X#endif
  2728. X#ifdef TERSE
  2729. X        fputs("\nNo articles.\n", stdout) FLUSH;
  2730. X#endif
  2731. X        unread_selector = 0;
  2732. X        mask = 1;
  2733. X    } else {
  2734. X#ifdef VERBOSE
  2735. X        IF (verbose)
  2736. X        fputs("\nNo unread articles to select.", stdout);
  2737. X        ELSE
  2738. X#endif
  2739. X#ifdef TERSE
  2740. X        fputs("\nNo articles.", stdout);
  2741. X#endif
  2742. X#ifndef USETMPTHREAD
  2743. X        if (tobethreaded) {
  2744. X        printf("  (%d article%s not yet threaded)",
  2745. X            tobethreaded, tobethreaded == 1 ? nullstr : "s") FLUSH;
  2746. X        }
  2747. X#endif
  2748. X        putchar('\n');    /* let "them" FLUSH */
  2749. X    }
  2750. X    (void) count_roots(TRUE);
  2751. X    art = art_hold;
  2752. X    p_art = curr_p_art;
  2753. X    return '\033';
  2754. X    }
  2755. X    if (unread_selector) {
  2756. X    for (j = 0; j < total.root; j++) {
  2757. X        selected_roots[j] |= 4;
  2758. X    }
  2759. X    }
  2760. X    if (page_root >= max_root) {
  2761. X    ch = '<';
  2762. X    } else {
  2763. X    ch = '>';
  2764. X    }
  2765. X    cur_root = 0;
  2766. X    running_total = 0;
  2767. X    for (i = 0; i < page_root; i++) {
  2768. X    running_total += root_article_cnts[i];
  2769. X    }
  2770. X    do {
  2771. X    select_chars = getval("SELECTCHARS", SELECTCHARS);
  2772. X    max_cnt = strlen(select_chars);
  2773. X    if (max_cnt > MAX_SEL) {
  2774. X        max_cnt = MAX_SEL;
  2775. X    }
  2776. X    if (ch == '<' && i) {
  2777. X        screen_line = 2;
  2778. X        cnt = 0;
  2779. X        /* Scan the roots in reverse to go back a page */
  2780. X        do {
  2781. X        if (!root_article_cnts[--i]) {
  2782. X            continue;
  2783. X        }
  2784. X        first = root_subjects[i];
  2785. X        last = first + p_roots[i].subject_cnt;
  2786. X        line_cnt = 0;
  2787. X        for (j = first; j < last; j++) {
  2788. X            line_cnt += count_subj_lines(i, j);
  2789. X        }
  2790. X        if (line_cnt > LINES - 5) {
  2791. X            line_cnt = LINES - 5;
  2792. X        }
  2793. X        screen_line += line_cnt;
  2794. X        if (screen_line > LINES - 3) {
  2795. X            i++;
  2796. X            break;
  2797. X        }
  2798. X        running_total -= root_article_cnts[i];
  2799. X        cnt++;
  2800. X        } while (i > 0 && cnt < max_cnt);
  2801. X    }
  2802. X
  2803. X    /* Present a page of subjects to the user */
  2804. X#ifndef CLEAREOL
  2805. X    clear();
  2806. X#else
  2807. X    if (can_home_clear) {
  2808. X        home_cursor();
  2809. X        maybe_eol();
  2810. X    } else {
  2811. X        clear();
  2812. X    }
  2813. X#endif
  2814. X    carriage_return();
  2815. X    page_root = i;
  2816. X    last_running = running_total;
  2817. X#ifdef NOFIREWORKS
  2818. X    no_sofire();
  2819. X#endif
  2820. X    standout();
  2821. X    fputs(ngname, stdout);
  2822. X    un_standout();
  2823. X    printf("          %ld %sarticle%s%s\n", (long)article_count,
  2824. X        unread_selector? "read " : nullstr,
  2825. X        article_count == 1 ? nullstr : "s", moderated);
  2826. X#ifdef CLEAREOL
  2827. X    maybe_eol();
  2828. X#endif
  2829. X    putchar('\n') FLUSH;
  2830. X    screen_line = 2;
  2831. X    for (cnt = 0; i < max_root && cnt < max_cnt; i++) {
  2832. X        if (last_root == i) {
  2833. X        cur_root = cnt;
  2834. X        }
  2835. X        /* Check each root for articles to list */
  2836. X        if (!root_article_cnts[i]) {
  2837. X        continue;
  2838. X        }
  2839. X        first = root_subjects[i];
  2840. X        last = first + p_roots[i].subject_cnt;
  2841. X
  2842. X        /* Compute how many lines we need to display the subjects/authors */
  2843. X        etc = FALSE;
  2844. X        line_cnt = 0;
  2845. X        for (j = first; j < last; j++) {
  2846. X        subj_line_cnt = count_subj_lines(i, j);
  2847. X        line_cnt += subj_line_cnt;
  2848. X        /* If this root is too long to fit on the screen all by
  2849. X        ** itself, trim it to fit and set the "etc" flag.
  2850. X        */
  2851. X        if (line_cnt > LINES - 5) {
  2852. X            last = j;
  2853. X            line_cnt -= subj_line_cnt;
  2854. X            if (line_cnt != LINES - 5) {
  2855. X            last++;
  2856. X            line_cnt = LINES - 5;
  2857. X            }
  2858. X            if (screen_line == 2) {
  2859. X            etc = TRUE;
  2860. X            }
  2861. X            break;
  2862. X        }
  2863. X        }
  2864. X        /* If it doesn't fit, save it for the next page */
  2865. X        if (screen_line + line_cnt > LINES - 3) {
  2866. X        break;
  2867. X        }
  2868. X        /* Output the subjects, with optional authors */
  2869. X        root_line[cnt] = screen_line;
  2870. X        running_total += root_article_cnts[i];
  2871. X        first_two_chars[0] = select_chars[cnt];
  2872. X        first_two_chars[1] = (selected_roots[i] & 4) ? '-' :
  2873. X                 (selected_roots[i] & mask) ? '+' : ' ';
  2874. X        author_line = screen_line;
  2875. X        for (j = first; j < last; j++) {
  2876. X        display_subj(i, j);
  2877. X        }
  2878. X        screen_line += line_cnt;
  2879. X        root_hold[cnt++] = i;
  2880. X        if (etc) {
  2881. X        fputs("      ...etc.", stdout);
  2882. X        i++;
  2883. X        break;
  2884. X        }
  2885. X    }/* for */
  2886. X    last_root = -1;
  2887. X    if (cur_root && cur_root >= cnt) {
  2888. X        cur_root = cnt - 1;
  2889. X    }
  2890. X
  2891. X    /* Check if there is really anything left to display. */
  2892. X    if (!running_total && !empty_ok) {
  2893. X        goto all_empty;
  2894. X    }
  2895. X    empty_ok = FALSE;
  2896. X
  2897. X    last_line = screen_line+1;
  2898. X#ifdef CLEAREOL
  2899. X    maybe_eol();
  2900. X#endif
  2901. X    putchar('\n') FLUSH;
  2902. X    /* Prompt the user */
  2903. X#ifdef MAILCALL
  2904. X    setmail();
  2905. X#endif
  2906. X    if (i != max_root) {
  2907. X        sprintf(promptbuf, "%s-- Select threads -- %s%ld%% [%c%c] --",
  2908. X        mailcall, (!page_root? "Top " : nullstr),
  2909. X        (long)(running_total*100 / article_count),
  2910. X        page_char, end_char);
  2911. X    } else {
  2912. X        sprintf(promptbuf, "%s-- Select threads -- %s [%c%c] --",
  2913. X        mailcall, (!page_root? "All" : "Bot"), end_char, page_char);
  2914. X    }
  2915. X    if (cur_root > cnt) {
  2916. X        cur_root = 0;
  2917. X    }
  2918. X    screen_line = root_line[cur_root];
  2919. X#ifdef CLEAREOL
  2920. X    if (erase_screen && can_home_clear) {
  2921. X        clear_rest();
  2922. X    }
  2923. X#endif
  2924. X    displayed_status = FALSE;
  2925. X      prompt_select:
  2926. X    standout();
  2927. X    fputs(promptbuf, stdout);
  2928. X    un_standout();
  2929. X    if (can_home) {
  2930. X        carriage_return();
  2931. X        goto_line(last_line, screen_line);
  2932. X    }
  2933. X    got_dash = 0;
  2934. X    /* Grab some commands from the user */
  2935. X    for (;;) {
  2936. X        fflush(stdout);
  2937. X        eat_typeahead();
  2938. X#ifdef CONDSUB
  2939. X        getcmd(buf);
  2940. X        ch = *buf;
  2941. X#else
  2942. X        getcmd(tmpbuf);    /* If no conditionals, don't allow macros */ 
  2943. X        ch = *tmpbuf;
  2944. X        buf[0] = ch;
  2945. X        buf[1] = FINISHCMD;
  2946. X#endif
  2947. X        if (errno) {
  2948. X        ch = Ctl('l');
  2949. X        }
  2950. X        in_select = index(select_chars, ch);
  2951. X        /* Plaster any inherited empty roots on first command if not Esc. */
  2952. X        if (cmd && (in_select || (ch != '\033' && ch != '+'))) {
  2953. X        max_root = count_roots(TRUE);
  2954. X        cmd = 0;
  2955. X        }
  2956. X        if (displayed_status && can_home) {
  2957. X        goto_line(screen_line, last_line+1);
  2958. X        erase_eol();
  2959. X        screen_line = last_line+1;
  2960. X        displayed_status = FALSE;
  2961. X        }
  2962. X        if (ch == '-') {
  2963. X        got_dash = 1;
  2964. X        if (!can_home) {
  2965. X            putchar('-');
  2966. X            fflush(stdout);
  2967. X        }
  2968. X        continue;
  2969. X        }
  2970. X        if (ch == ' ') {
  2971. X        if (i == max_root) {
  2972. X            ch = end_char;
  2973. X        } else {
  2974. X            ch = page_char;
  2975. X        }
  2976. X        }
  2977. X        if (!in_select && (index("<+>^$!?&:/hDJLNPqQTUXZ\n\r\t\033", ch)
  2978. X         || ch == Ctl('l') || ch == Ctl('r') || ch == Ctl('k'))) {
  2979. X        break;
  2980. X        }
  2981. X        if (in_select) {
  2982. X        j = in_select - select_chars;
  2983. X        if (j >= cnt) {
  2984. X            dingaling();
  2985. X            j = -1;
  2986. X        } else if (got_dash) {
  2987. X            ;
  2988. X        } else if (selected_roots[root_hold[j]] & mask) {
  2989. X            action = (unread_selector ? 'k' : '-');
  2990. X        } else {
  2991. X            action = '+';
  2992. X        }
  2993. X        } else if (ch == 'y' || ch == '.') {
  2994. X        j = cur_root;
  2995. X        if (selected_roots[root_hold[j]] & mask) {
  2996. X            action = (unread_selector ? 'k' : '-');
  2997. X        } else {
  2998. X            action = '+';
  2999. X        }
  3000. X        } else if (ch == 'k' || ch == 'j' || ch == ',') {
  3001. X        j = cur_root;
  3002. X        action = 'k';
  3003. X        } else if (ch == 'm' || ch == '\\') {
  3004. X        j = cur_root;
  3005. X        action = 'm';
  3006. X        } else if (ch == '@') {
  3007. X        cur_root = 0;
  3008. X        j = cnt-1;
  3009. X        got_dash = 1;
  3010. X        action = '@';
  3011. X        } else if (ch == '[' || ch == 'p') {
  3012. X        if (--cur_root < 0) {
  3013. X            cur_root = cnt ? cnt-1 : 0;
  3014. X        }
  3015. X        j = -1;
  3016. X        } else if (ch == ']' || ch == 'n') {
  3017. X        if (++cur_root >= cnt) {
  3018. X            cur_root = 0;
  3019. X        }
  3020. X        j = -1;
  3021. X        } else {
  3022. X        if (can_home) {
  3023. X            goto_line(screen_line, last_line+1);
  3024. X            screen_line = last_line+1;
  3025. X        } else {
  3026. X            putchar('\n');
  3027. X        }
  3028. X        printf("Type ? for help.");
  3029. X        settle_down();
  3030. X        displayed_status = TRUE;
  3031. X
  3032. X        if (can_home) {
  3033. X            carriage_return();
  3034. X        } else {
  3035. X            putchar('\n');
  3036. X        }
  3037. X        j = -1;
  3038. X        }
  3039. X        if (j >= 0) {
  3040. X        if (!got_dash) {
  3041. X            cur_root = j;
  3042. X        } else {
  3043. X            got_dash = 0;
  3044. X            if (j < cur_root) {
  3045. X            ch = cur_root-1;
  3046. X            cur_root = j;
  3047. X            j = ch;
  3048. X            }
  3049. X        }
  3050. X        if (++j == cnt) {
  3051. X            j = 0;
  3052. X        }
  3053. X        do {
  3054. X          register int r;
  3055. X          register char maskr = mask;
  3056. X            r = root_hold[cur_root];
  3057. X            if (can_home) {
  3058. X            goto_line(screen_line, root_line[cur_root]);
  3059. X            screen_line = root_line[cur_root];
  3060. X            }
  3061. X            putchar(select_chars[cur_root]);
  3062. X            if (action == '@') {
  3063. X            if (selected_roots[r] & 4) {
  3064. X                ch = (unread_selector ? '+' : ' ');
  3065. X            } else if (unread_selector) {
  3066. X                ch = 'k';
  3067. X            } else
  3068. X            if (selected_roots[r] & maskr) {
  3069. X                ch = '-';
  3070. X            } else {
  3071. X                ch = '+';
  3072. X            }
  3073. X            } else {
  3074. X            ch = action;
  3075. X            }
  3076. X            switch (ch) {
  3077. X            case '+':
  3078. X            if (!(selected_roots[r] & maskr)) {
  3079. X                selected_roots[r] |= maskr;
  3080. X                selected_root_cnt++;
  3081. X                selected_count += root_article_cnts[r];
  3082. X                putchar('+');
  3083. X            }
  3084. X            /* FALL THROUGH */
  3085. X            case 'm':
  3086. X            if (selected_roots[r] & 4) {
  3087. X                selected_roots[r] &= ~4;
  3088. X                if (ch == 'm') {
  3089. X                putchar(' ');
  3090. X                }
  3091. X            } else if (ch == 'm') {
  3092. X                goto unsel;
  3093. X            }
  3094. X            break;
  3095. X            case 'k':
  3096. X            if (!(selected_roots[r] & 4)) {
  3097. X                selected_roots[r] |= 4;
  3098. X                putchar('-'), fflush(stdout);
  3099. X                p_art = p_articles + p_roots[r].articles;
  3100. X                art = 0;
  3101. X                follow_thread('x');
  3102. X            }
  3103. X            /* FALL THROUGH */
  3104. X            case '-':
  3105. X            unsel:
  3106. X            if (selected_roots[r] & maskr) {
  3107. X                selected_roots[r] &= ~maskr;
  3108. X                selected_root_cnt--;
  3109. X                selected_count -= root_article_cnts[r];
  3110. X                if (ch != 'k') {
  3111. X                putchar(' ');
  3112. X                }
  3113. X            }
  3114. X            break;
  3115. X            }
  3116. X            fflush(stdout);
  3117. X            if (++cur_root == cnt) {
  3118. X            cur_root = 0;
  3119. X            }
  3120. X            if (can_home) {
  3121. X            carriage_return();
  3122. X            }
  3123. X        } while (cur_root != j);
  3124. X        } else {
  3125. X        got_dash = FALSE;
  3126. X        }
  3127. X        if (can_home) {
  3128. X        goto_line(screen_line, root_line[cur_root]);
  3129. X        screen_line = root_line[cur_root];
  3130. X        }
  3131. X    }/* for */
  3132. X    if (can_home) {
  3133. X        goto_line(screen_line, last_line);
  3134. X    }
  3135. X    clean_screen = TRUE;
  3136. X      do_command:
  3137. X    output_chase_phrase = TRUE;
  3138. X    if (ch == 'L') {
  3139. X        if (!*++display_mode) {
  3140. X        display_mode = select_order;
  3141. X        }
  3142. X        ch = Ctl('l');
  3143. X        cur_root = 0;
  3144. X    } else if (ch == '$') {
  3145. X        ch = '<';
  3146. X        page_root = max_root;
  3147. X        last_running = article_count;
  3148. X        cur_root = 0;
  3149. X    } else if (ch == '^' || ch == Ctl('r')) {
  3150. X        ch = '>';
  3151. X        i = 0;
  3152. X        running_total = 0;
  3153. X        cur_root = 0;
  3154. X    } else if (ch == 'h' || ch == '?') {
  3155. X        putchar('\n');
  3156. X        if ((ch = help_select()) || (ch = pause_getcmd())) {
  3157. X        goto got_cmd;
  3158. X        }
  3159. X        ch = Ctl('l');
  3160. X    } else if (index(":/&!", ch)) {
  3161. X        erase_eol();        /* erase the prompt */
  3162. X        if (!finish_command(TRUE)) {    /* get rest of command */
  3163. X        if (clean_screen) {
  3164. X            screen_line = root_line[cur_root];
  3165. X            goto prompt_select;
  3166. X        }
  3167. X        goto extend_done;
  3168. X        }
  3169. X        if (ch == '&' || ch == '!') {
  3170. X        one_command = TRUE;
  3171. X        perform(buf, FALSE);
  3172. X        one_command = FALSE;
  3173. X
  3174. X        putchar('\n') FLUSH;
  3175. X        clean_screen = FALSE;
  3176. X        } else {
  3177. X        int selected_save = selected_root_cnt;
  3178. X
  3179. X        if (ch == ':') {
  3180. X            clean_screen = (use_selected() == 2) && clean_screen;
  3181. X            if (!unread_selector) {
  3182. X            for (j = 0; j < total.root; j++) {
  3183. X                if (selected_roots[j] & 4) {
  3184. X                selected_roots[j] = 0;
  3185. X                p_art = p_articles + p_roots[j].articles;
  3186. X                art = 0;
  3187. X                follow_thread('j');
  3188. X                }
  3189. X            }
  3190. X            }
  3191. X        } else {
  3192. X            /* Force the search to begin at absfirst or firstart,
  3193. X            ** depending upon whether they specified the 'r' option.
  3194. X            */
  3195. X            art = lastart+1;
  3196. X            page_line = 1;
  3197. X            switch (art_search(buf, sizeof buf, FALSE)) {
  3198. X            case SRCH_ERROR:
  3199. X            case SRCH_ABORT:
  3200. X            case SRCH_INTR:
  3201. X            fputs("\nInterrupted\n", stdout) FLUSH;
  3202. X            break;
  3203. X            case SRCH_DONE:
  3204. X            case SRCH_SUBJDONE:
  3205. X            fputs("Done\n", stdout) FLUSH;
  3206. X            break;
  3207. X            case SRCH_NOTFOUND:
  3208. X            fputs("\nNot found.\n", stdout) FLUSH;
  3209. X            break;
  3210. X            case SRCH_FOUND:
  3211. X            break;
  3212. X            }
  3213. X            clean_screen = FALSE;
  3214. X        }
  3215. X        /* Recount, in case something has changed. */
  3216. X        max_root = count_roots(!unread_selector);
  3217. X
  3218. X        running_total = 0;
  3219. X        if (article_count) {
  3220. X            for (j = 0; j < page_root; j++) {
  3221. X            running_total += root_article_cnts[j];
  3222. X            }
  3223. X        }
  3224. X        cur_root = 0;
  3225. X
  3226. X        if ((selected_save -= selected_root_cnt) != 0) {
  3227. X            putchar('\n');
  3228. X            if (selected_save < 0) {
  3229. X            fputs("S", stdout);
  3230. X            selected_save *= -1;
  3231. X            } else {
  3232. X            fputs("Des", stdout);
  3233. X            }
  3234. X            printf("elected %d thread%s.", selected_save,
  3235. X            selected_save == 1 ? nullstr : "s");
  3236. X            clean_screen = FALSE;
  3237. X        }
  3238. X        if (!clean_screen) {
  3239. X            putchar('\n') FLUSH;
  3240. X        }
  3241. X        }/* if !& or :/ */
  3242. X
  3243. X        if (clean_screen) {
  3244. X        carriage_return();
  3245. X        up_line();
  3246. X        erase_eol();
  3247. X        screen_line = root_line[cur_root];
  3248. X        goto prompt_select;
  3249. X        }
  3250. X      extend_done:
  3251. X        if ((ch = pause_getcmd())) {
  3252. X          got_cmd:
  3253. X        if (ch > 0) {
  3254. X            /* try to optimize the screen update for some commands. */
  3255. X            if (!index(select_chars, ch)
  3256. X             && (index("<+>^$!?&:/hDJLNPqQTUXZ\n\r\t\033", ch)
  3257. X              || ch == Ctl('k'))) {
  3258. X            buf[0] = ch;
  3259. X            buf[1] = FINISHCMD;
  3260. X            goto do_command;
  3261. X            }
  3262. X            pushchar(ch | 0200);
  3263. X        }
  3264. X        }
  3265. X        ch = Ctl('l');
  3266. X    } else if (ch == Ctl('k')) {
  3267. X        edit_kfile();
  3268. X        ch = Ctl('l');
  3269. X    } else if (!unread_selector && (ch == 'X' || ch == 'D' || ch == 'J')) {
  3270. X        if (ch == 'D') {
  3271. X        j = page_root;
  3272. X        last = i;
  3273. X        } else {
  3274. X        j = 0;
  3275. X        last = max_root;
  3276. X        }
  3277. X        for (; j < last; j++) {
  3278. X        if (((!(selected_roots[j] & 1) ^ (ch == 'J'))
  3279. X         && (cnt = root_article_cnts[j])) || (selected_roots[j] & 4)) {
  3280. X            p_art = p_articles + p_roots[j].articles;
  3281. X            art = 0;
  3282. X            follow_thread((selected_roots[j] & 4) ? 'j' : 'J');
  3283. X        }
  3284. X        }
  3285. X        max_root = count_roots(TRUE);
  3286. X        if (article_count
  3287. X         && (ch == 'J' || (ch == 'D' && !selected_root_cnt))) {
  3288. X        last_running = 0;
  3289. X        for (i = 0; i < page_root; i++) {
  3290. X            last_running += root_article_cnts[i];
  3291. X        }
  3292. X        ch = Ctl('l');
  3293. X        cur_root = 0;
  3294. X        } else {
  3295. X        if (!output_chase_phrase)
  3296. X            ch = 'o';
  3297. X        break;
  3298. X        }
  3299. X    } else if (ch == 'J') {
  3300. X        for (j = 0; j < max_root; j++) {
  3301. X        selected_roots[j] = (selected_roots[j] & ~2) | 4;
  3302. X        }
  3303. X        selected_root_cnt = selected_count = 0;
  3304. X        ch = Ctl('l');
  3305. X    } else if (ch == 'T') {
  3306. X      register int r;
  3307. X
  3308. X        erase_eol();        /* erase the prompt */
  3309. X        r = root_hold[cur_root];
  3310. X        p_art = p_articles + p_roots[r].articles;
  3311. X        art = p_art->num;
  3312. X        if (p_art->subject == -1) {
  3313. X        follow_thread('N');
  3314. X        }
  3315. X        perform("T", FALSE);
  3316. X        max_root = count_roots(TRUE);
  3317. X        if (article_count) {
  3318. X        ch = Ctl('l');
  3319. X        }
  3320. X    }
  3321. X    if (ch == '>') {
  3322. X        cur_root = 0;
  3323. X        page_root = i;
  3324. X    } else if (ch == '<' || (page_root && page_root >= max_root)) {
  3325. X        cur_root = 0;
  3326. X        running_total = last_running;
  3327. X        if (!(i = page_root) || !max_root) {
  3328. X        ch = '>';
  3329. X        } else {
  3330. X        ch = '<';
  3331. X        }
  3332. X    } else if (ch == Ctl('l')) {
  3333. X        i = page_root;
  3334. X        running_total = last_running;
  3335. X        ch = '>';
  3336. X    } else if (ch == '\r' || ch == '\n') {
  3337. X        if (!selected_root_cnt) {
  3338. X          register r = root_hold[cur_root];
  3339. X        selected_roots[r] = mask;
  3340. X        selected_root_cnt++;
  3341. X        selected_count += root_article_cnts[r];
  3342. X        }
  3343. X    }
  3344. X    } while ((ch == '>' && i < max_root) || ch == '<');
  3345. X    if (ch != 'o') {
  3346. X    putchar('\n') FLUSH;
  3347. X    }
  3348. X
  3349. X    if (unread_selector) {
  3350. X    /* Turn selections into unread selected roots.  Let count_roots()
  3351. X    ** fix the counts after we're through.
  3352. X    */
  3353. X    last_root = -1;
  3354. X    for (j = 0; j < total.root; j++) {
  3355. X        if (!(selected_roots[j] & 4)) {
  3356. X        if (selected_roots[j] & 2) {
  3357. X            selected_roots[j] = 1;
  3358. X        }
  3359. X        p_art = p_articles + p_roots[j].articles;
  3360. X        art = 0;
  3361. X        follow_thread('u');
  3362. X        } else {
  3363. X        selected_roots[j] &= ~4;
  3364. X        }
  3365. X    }
  3366. X    } else {
  3367. X    select_page = page_root;
  3368. X    for (j = 0; j < total.root; j++) {
  3369. X        if (selected_roots[j] & 4) {
  3370. X        selected_roots[j] = 0;
  3371. X        p_art = p_articles + p_roots[j].articles;
  3372. X        art = 0;
  3373. X        follow_thread('j');
  3374. X        }
  3375. X    }
  3376. X    }
  3377. X    if (ch == 'U') {
  3378. X    unread_selector = !unread_selector;
  3379. X    empty_ok = TRUE;
  3380. X    goto select_threads;
  3381. X    }
  3382. X
  3383. X    if (unread_selector) {
  3384. X    unread_selector = 0;
  3385. X    mask = 1;
  3386. X    (void) count_roots(FALSE);
  3387. X    }
  3388. X    if (ch == 'N' || ch == 'P' || Ctl(ch) == Ctl('q') || ch == '\033') {
  3389. X    art = art_hold;
  3390. X    p_art = curr_p_art;
  3391. X    } else {
  3392. X    first_art();
  3393. X    }
  3394. X    clear_on_stop = FALSE;
  3395. X    mode = oldmode;
  3396. X    return ch;
  3397. X}
  3398. X
  3399. static int author_cnt, first_author;
  3400. X
  3401. X/* Counts the number of lines needed to output a subject, including optional
  3402. X** authors.
  3403. X*/
  3404. static int
  3405. count_subj_lines(root, subj)
  3406. int root;
  3407. int subj;
  3408. X{
  3409. X    PACKED_ARTICLE *artp, *root_limit;
  3410. X    int author_subj;
  3411. X
  3412. X    author_cnt = 0;
  3413. X    author_subj = subj;
  3414. X    first_author = -1;
  3415. X
  3416. X    if (!subject_cnts[subj]) {
  3417. X    return 0;
  3418. X    }
  3419. X    if (*display_mode == 's') {    /* no-author mode takes one line */
  3420. X    return ++author_cnt;
  3421. X    }
  3422. X    bzero(author_cnts, total.author * sizeof (WORD));
  3423. X
  3424. X    /* Count authors associated with this subject.  Increments author_cnts. */
  3425. X    artp = p_articles + p_roots[root].articles;
  3426. X    root_limit = upper_limit(artp, FALSE);
  3427. X    for (; artp != root_limit; artp++) {
  3428. X    if (artp->subject == author_subj
  3429. X     && (!was_read(artp->num) ^ unread_selector)) {
  3430. X        if (artp->author < 0 || artp->author >= total.author) {
  3431. X        printf("\
  3432. XFound invalid author (%d) with valid subject (%d)! [%ld]\n",
  3433. X            artp->author, artp->subject, artp->num);
  3434. X        artp->author = 0;
  3435. X        } else {
  3436. X        if (first_author < 0) {
  3437. X            first_author = artp->author;
  3438. X        }
  3439. X        if (!author_cnts[artp->author]++) {
  3440. X            author_cnt++;
  3441. X        }
  3442. X        }
  3443. X    }
  3444. X    }
  3445. X
  3446. X    if (*display_mode == 'm') {
  3447. X    return (author_cnt+4)/3;
  3448. X    } else {
  3449. X    return author_cnt;
  3450. X    }
  3451. X}
  3452. X
  3453. static void
  3454. display_subj(root, subj)
  3455. int root;
  3456. int subj;
  3457. X{
  3458. X    PACKED_ARTICLE *artp, *root_limit;
  3459. X    char *str;
  3460. X
  3461. X    count_subj_lines(root, subj);
  3462. X    if (!author_cnt) {
  3463. X    return;
  3464. X    }
  3465. X    artp = p_articles + p_roots[root].articles;
  3466. X    if (artp->subject != -1 && (artp->flags & ROOT_ARTICLE)
  3467. X     && (!was_read(artp->num) ^ unread_selector)) {
  3468. X    str = nullstr;
  3469. X    } else {
  3470. X    str = ">";
  3471. X    }
  3472. X#ifdef CLEAREOL
  3473. X    maybe_eol();
  3474. X#endif
  3475. X    if (*display_mode == 's') {
  3476. X    printf("%s%3d  %s%.71s\n", first_two_chars,
  3477. X        subject_cnts[subj], str, subject_ptrs[subj]) FLUSH;
  3478. X    } else {
  3479. X    printf("%s%-16.16s%3d  %s%.55s", first_two_chars,
  3480. X        author_ptrs[first_author],
  3481. X        subject_cnts[subj], str, subject_ptrs[subj]);
  3482. X    if (author_cnt > 1) {
  3483. X        author_cnts[first_author] = 0;
  3484. X        author_cnt = 0;
  3485. X        root_limit = upper_limit(artp, FALSE);
  3486. X        for (; artp != root_limit; artp++) {
  3487. X        if (artp->author >= 0 && author_cnts[artp->author]) {
  3488. X            switch (author_cnt % 3) {
  3489. X            case 0:
  3490. X            putchar('\n') FLUSH;
  3491. X            if (++author_line >= LINES - 3) {
  3492. X                return;
  3493. X            }
  3494. X#ifdef CLEAREOL
  3495. X            maybe_eol();
  3496. X#endif
  3497. X            putchar(' ');
  3498. X            putchar(' ');
  3499. X            break;
  3500. X            case 1:
  3501. X            putchar('\t');
  3502. X            putchar('\t');
  3503. X            break;
  3504. X            case 2:
  3505. X            putchar('\t');
  3506. X            break;
  3507. X            }
  3508. X            author_cnt += (*display_mode == 'm');
  3509. X            printf("%-16.16s", author_ptrs[artp->author]);
  3510. X            author_cnts[artp->author] = 0;
  3511. X        }/* if */
  3512. X        }/* for */
  3513. X    }/* if */
  3514. X    putchar('\n') FLUSH;
  3515. X    author_line++;
  3516. X    }/* if */
  3517. X    first_two_chars[0] = first_two_chars[1] = ' ';
  3518. X}
  3519. X
  3520. X/* Get each root's article count, and subject count(s); count total
  3521. X** articles and selected articles (use unread_selector to determine
  3522. X** whether to count read or unread articles); deselect any roots we
  3523. X** find that are empty (if do_unselect is TRUE); find the last non-
  3524. X** empty root, and return its count (the index+1).
  3525. X*/
  3526. int
  3527. count_roots(do_unselect)
  3528. bool_int do_unselect;
  3529. X{
  3530. X    register int count;
  3531. X    register PACKED_ARTICLE *artp, *root_limit, *art_limit;
  3532. X    int last_root = -1;
  3533. X
  3534. X    article_count = selected_count = selected_root_cnt = 0;
  3535. X
  3536. X    if (!(artp = p_articles)) {
  3537. X    return 0;
  3538. X    }
  3539. X    art_limit = artp + total.article;
  3540. X    root_limit = upper_limit(artp, 0);
  3541. X
  3542. X    bzero(subject_cnts, total.subject * sizeof (WORD));
  3543. X    count = 0;
  3544. X
  3545. X    for (;;) {
  3546. X    if (artp->subject == -1) {
  3547. X        if (!was_read(artp->num)) {
  3548. X        oneless(artp->num);
  3549. X        }
  3550. X    } else if ((!was_read(artp->num) ^ unread_selector)) {
  3551. X        count++;
  3552. X        subject_cnts[artp->subject]++;
  3553. X    }
  3554. X    if (++artp == root_limit) {
  3555. X        register int root_num = artp[-1].root;
  3556. X        register char maskr = mask;
  3557. X
  3558. X        root_article_cnts[root_num] = count;
  3559. X        if (count) {
  3560. X        article_count += count;
  3561. X        if (selected_roots[root_num] & maskr) {
  3562. X            selected_roots[root_num] &= ~4;
  3563. X            selected_root_cnt++;
  3564. X            selected_count += count;
  3565. X        }
  3566. X        last_root = root_num;
  3567. X        } else if (do_unselect) {
  3568. X        selected_roots[root_num] &= ~maskr;
  3569. X        } else if (selected_roots[root_num] & maskr) {
  3570. X        selected_roots[root_num] &= ~4;
  3571. X        selected_root_cnt++;
  3572. X        }
  3573. X        if (artp == art_limit) {
  3574. X        break;
  3575. X        }
  3576. X        root_limit = upper_limit(artp, 0);
  3577. X        count = 0;
  3578. X    }
  3579. X    }
  3580. X    if (do_unselect) {
  3581. X    scan_all_roots = !article_count;
  3582. X    }
  3583. X    unthreaded = toread[ng] - article_count;
  3584. X
  3585. X    return last_root+1;
  3586. X}
  3587. X
  3588. X/* Count the unread articles attached to the given root number.
  3589. X*/
  3590. int
  3591. count_one_root(root_num)
  3592. int root_num;
  3593. X{
  3594. X    int last = (root_num == total.root-1 ? total.article
  3595. X                     : p_roots[root_num+1].articles);
  3596. X    register int count = 0, i;
  3597. X
  3598. X    for (i = p_roots[root_num].articles; i < last; i++) {
  3599. X    if (p_articles[i].subject != -1 && !was_read(p_articles[i].num)) {
  3600. X        count++;
  3601. X    }
  3602. X    }
  3603. X    root_article_cnts[root_num] = count;
  3604. X
  3605. X    return count;
  3606. X}
  3607. X
  3608. X#endif /* USETHREADS */
  3609. END_OF_FILE
  3610. if test 23184 -ne `wc -c <'rt-select.c'`; then
  3611.     echo shar: \"'rt-select.c'\" unpacked with wrong size!
  3612. fi
  3613. # end of 'rt-select.c'
  3614. fi
  3615. echo shar: End of archive 7 \(of 13\).
  3616. cp /dev/null ark7isdone
  3617. MISSING=""
  3618. for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 ; do
  3619.     if test ! -f ark${I}isdone ; then
  3620.     MISSING="${MISSING} ${I}"
  3621.     fi
  3622. done
  3623. if test "${MISSING}" = "" ; then
  3624.     echo You have unpacked all 13 archives.
  3625.     rm -f ark[1-9]isdone ark[1-9][0-9]isdone
  3626. else
  3627.     echo You still need to unpack the following archives:
  3628.     echo "        " ${MISSING}
  3629. fi
  3630. ##  End of shell archive.
  3631. exit 0
  3632.